home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 12 / Cream of the Crop 12 (Part II) / Cream of the Crop 12 (Part II).iso / OS2 / VD08DOC1.ZIP / usr / lib / book / tutorial.inf (.txt) < prev   
Encoding:
OS/2 Help File  |  1996-02-13  |  373.1 KB  |  4,468 lines

  1.  
  2. ΓòÉΓòÉΓòÉ 1. Abstract ΓòÉΓòÉΓòÉ
  3.  
  4. This manual is an introduction into OS/2 PM programming using Objective C and 
  5. this class library. In addition to explaining the basics of the PM class 
  6. library, an overview of the database library and the more generic classes is 
  7. provided. In later sections, the development tools supporting some kind of 
  8. visual programming are introduced. 
  9.  
  10. Before beginning to use this library in application development, you should 
  11. read this manual carefully. Most of the sample programs are explained here in 
  12. detail, a fact that can save you lots of time from studying the source code of 
  13. the samples itself. 
  14.  
  15. At the end of this document, you can find some recommendations, which books to 
  16. read, if you have any specific questions concerning Objective C, or OS/2 
  17. Programming. 
  18.  
  19. If you already know the principles on which the class libraries are built on, 
  20. you might better look into the Reference Manual for specific information. 
  21.  
  22. If you are searching for specific information concerning 
  23.  
  24.      Installation ... Read the Installation Manual. 
  25.      Basics of Application development ... Read the appropriate sections in 
  26.       this manual, the Tutorial. Here you can find a gentle introduction into 
  27.       using this library package for developing OS/2 PM applications. 
  28.      Classes and Methods provided by the libraries ... You can find special 
  29.       information about the provided classes and methods in the Reference 
  30.       Manual. 
  31.      References on the graphical development tools (database editor, interface 
  32.       editor, project editor) ... Read the appropriate sections in the 
  33.       Application Programming Tools Manual. 
  34.      Literature ... Look in the Literature section of this Manual. 
  35.  headings roman page1 arabic page1 
  36.  
  37.  
  38. ΓòÉΓòÉΓòÉ 2. Introduction ΓòÉΓòÉΓòÉ
  39.  
  40. Programming OS/2 PM applications is mostly done using the programming language 
  41. C. Because the OS/2 application programming interface (API) is in most parts 
  42. object oriented, more and more programmers choose an object oriented 
  43. programming language for their purposes. The most popular object oriented 
  44. programming language for development of OS/2 applications today is C++. 
  45.  
  46. Because of the mostly static binding and its nearly completely missing run-time 
  47. system many people are searching for easy-to use alternatives to C++. One of 
  48. the most popular alternatives in object oriented programming to C++ is 
  49. Smalltalk. Due to its features, such as dynamic binding, messaging, etc., it is 
  50. better suited for developing complex applications using a graphical user 
  51. interface with PM. 
  52.  
  53. There is another object oriented programming language, which is as easy to 
  54. learn as pure C (because it is not much more than C itself), but supports 
  55. dynamic binding just like Smalltalk does. This language is Objective C. 
  56.  
  57. Objective C only adds some few new features to its "father" C, so it is an easy 
  58. to learn language for C programmers. 
  59.  
  60. Another advantage of Objective C is that an Objective C compiler is part of 
  61. GCC, the GNU C compiler. 
  62.  
  63. So---get it and start developing native OS/2 32bit programs using Objective C. 
  64.  
  65. This document is a simple---not yet complete---tutorial, showing you how to 
  66. start using this library package, and also showing some of the basic classes 
  67. provided. It is by no means a reference manual, if you're searching for some 
  68. special information, look in the  Reference Manual which you should also have 
  69. received. In addition to an introduction into the concepts of the libraries 
  70. themselves, later chapters will show an alternate way of developing OS/2 PM 
  71. applications using Objective C. By making use of the development tools shipped 
  72. together with the libraries some kind of visual development can be performed. 
  73.  
  74. Therefore freed from routine jobs, just as writing a Makefile, the programmer 
  75. can concentrate on his "real" job, writing a simple and easy-to-use 
  76. application. By support of rapid prototyping of the graphical user interface on 
  77. the one hand, and even the classes used by the application and the creating and 
  78. linking of objects on the other hand, OS/2 applications can be created within 
  79. some minutes. Fully utilizing the existing tools, your application is just a 
  80. few mouse clicks away In the end, some hints on debugging Objective C programs 
  81. are given. Be sure to read this section, this can save you a lot of troubles 
  82. when developing applications. 
  83.  
  84. This document assumes the reader is familiar with the programming language C, 
  85. and---to some extent---of Objective C. There are many good books on the C 
  86. language, but the situation for the Objective C language seems to be a bit 
  87. worse---not to say catastrophic. Sources of information can be found in the 
  88. Objective C FAQ regularly posted in comp.lang.objective-c. There you can also 
  89. find a gentle introduction into this object-oriented flavour of C and many 
  90. pointers to sources of information. 
  91.  
  92. It must be stated, that writing multi-threaded applications using Objective C 
  93. is not that simple at the moment. The Objective C runtime system is not 
  94. thread-safe now, so only one thread (normally the main thread) is allowed to 
  95. use the runtime library (and therefore Objective C objects). As this problem is 
  96. worked on at the moment, I am sure writing multi-threaded apps will be possible 
  97. soon. 
  98.  
  99.  
  100. ΓòÉΓòÉΓòÉ 3. Writing a Simple PM Application ΓòÉΓòÉΓòÉ
  101.  
  102. Programming OS/2 Presentation Manager can be a quite hard job, if you rely on 
  103. pure C and the OS/2 API functions. This is why I developed this class 
  104. libraries. As you will see throughout this and the following chapters, using 
  105. Objective C normally spares you the time to read the complex documentation of 
  106. the OS/2 Application Programming Interface. There are just some basics you 
  107. should know before starting development. 
  108.  
  109. Before doing any real work your program must do some initialization, which 
  110. means it has to allocate all necessary resources to run and it has to register 
  111. itself at the graphical subsystem of OS/2, the Presentation Manager (PM). 
  112.  
  113. After the program is run, all resources must be freed again. 
  114.  
  115. So, lets look at a simple PM application written using C 
  116.  
  117.  
  118. ΓòÉΓòÉΓòÉ 3.1. Application Main Function ΓòÉΓòÉΓòÉ
  119.  
  120. #define INCL_PM
  121. #include <os2.h>
  122. .
  123. .
  124.  
  125. main ()
  126. {
  127. HAB hab; /* handle to the anchor block of the application */
  128. HMQ hmq; /* handle to the main message queue of the appl. */
  129. QMSG qmsg; /* message structure */
  130.  
  131. hab = WinInitialize (0); /* register application at PM */
  132. hmq = WinCreateMsgQueue (hab,0);/* create main message queue */
  133.  
  134. .
  135. . /* other initialization, allocate resources, ... */
  136. .
  137.  
  138. while (WinGetMsg (hab,&qmsg,(HWND) NULL,0,0))
  139. WinDispatchMsg (hmq,&qmsg); /* process all messages */
  140.  
  141. . /*
  142. . * free all allocated resources,
  143. . * prepare application to terminate
  144. . */
  145.  
  146. WinDestroyMsgQueue (hmq); /* destroy main message queue */
  147. WinTerminate (hab); /* de-register application */
  148. }
  149.  
  150. This application skeleton shows the necessary steps, a program has to go 
  151. through to be run with OS/2 Presentation Manager. 
  152.  
  153.    1. Initialization: registration at PM, create message queue,~... 
  154.    2. Message loop: receive all messages for the application and process them 
  155.    3. Cleanup: destroy message queue, de-register application,~... 
  156.  
  157.  The Objective C PM class library provides a class, called StdApp to meet the 
  158.  purpose of standard initialization and message processing for every PM 
  159.  application. The following piece of source code demonstrates how to use it: 
  160.  
  161.   #include <pm/pm.h>
  162.   .
  163.   .
  164.   main ()
  165.   {
  166.   StdApp *application; /* pointer to our instance
  167.   of a StdApp class */
  168.  
  169.   application = [StdApp alloc]; /* create application object */
  170.  
  171.   [application init]; /* initialize application */
  172.   .
  173.   .
  174.   .
  175.   [application run]; /* process all messages */
  176.   .
  177.   .
  178.   .
  179.   [application free]; /* free application object */
  180.   }
  181.  
  182.  As you can see the first line of the sample includes <pm/pm.h>. This include 
  183.  file causes all include files of the PM class library to be read in. After 
  184.  doing this, you can use all classes of the library and their methods without 
  185.  any restrictions. 
  186.  
  187.  And here a more compact version of the same piece of code: 
  188.  
  189.   #include <pm/pm.h>
  190.   .
  191.   .
  192.   main ()
  193.   {
  194.   StdApp *application = [[StdApp alloc] init];
  195.   .
  196.   .
  197.   .
  198.   [application run];
  199.   .
  200.   .
  201.   .
  202.   [application free];
  203.   }
  204.  
  205.  As you can see using this class library can really simplify your life. Instead 
  206.  of creating and initializing dozens of local or---even worse---global 
  207.  variables, you simply allocate and initialize a single object. 
  208.  
  209.  
  210. ΓòÉΓòÉΓòÉ 3.2. A Simple Application ΓòÉΓòÉΓòÉ
  211.  
  212. To demonstrate the complexity of a complete PM application written in pure C, I 
  213. will show you a program that just creates a standard window, waits until this 
  214. window gets closed by the user and then terminates its operation. At first, 
  215. again the standard C version is shown, only using OS/2 API functions: 
  216.  
  217. #define INCL_PM
  218. #include <os2.h>
  219.  
  220. #define NEWCLASSNAME NewClass
  221.  
  222. MRESULT EXPENTRY windowFunction (HWND hwnd,ULONG msg,
  223. MPARAM mp1,MPARAM mp2)
  224. {
  225. switch (msg) {
  226. case WM_ERASEBACKGROUND:
  227. return (MRESULT) FALSE;
  228. default:
  229. return WinDefWindowProc (hwnd,msg,mp1,mp2);
  230. }
  231. }
  232.  
  233. main ()
  234. {
  235. HAB hab;
  236. HMQ hmq;
  237. QMSG qmsg;
  238. HWND mainWindow;
  239. HWND clientWindow;
  240. ULONG createFlags;
  241.  
  242. hab = WinInitialize (0);
  243. hmq = WinCreateMsgQueue (hab,0);
  244.  
  245. WinRegisterClass (hab,NEWCLASSNAME,windowFunction,0L,0);
  246.  
  247. createFlags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX |
  248. FCF_SIZEBORDER | FCF_SHELLPOSITION |
  249. FCF_TASKLIST;
  250.  
  251. mainWindow = WinCreateStdWindow (HWND_DESKTOP,
  252. WS_VISIBLE,
  253. &createFlags,
  254. (PSZ) NEWCLASSNAME,
  255. (PSZ) ,
  256. 0L,
  257. NULLHANDLE,
  258. 1000,
  259. &clientWindow);
  260.  
  261. while (WinGetMsg (hab,&qmsg,(HWND) NULL,0,0))
  262. WinDispatchMsg (hab,&qmsg);
  263.  
  264. WinDestroyWindow (mainWindow);
  265.  
  266. WinDestroyMsgQueue (hmq);
  267. WinTerminate (hab);
  268. }
  269.  
  270.  
  271.  
  272. Sample application "simple_o.exe"
  273.  
  274. The following piece source code shows how much simpler it is to use the PM 
  275. class library than working with "normal" OS/2 PM API functions. 
  276.  
  277. #include <pm/pm.h>
  278.  
  279. main ()
  280. {
  281. StdApp *application = [[StdApp alloc] init];
  282. MainWindow *mainWindow = [[MainWindow alloc]
  283. initWithId: 1000
  284. andFlags: (FCF_SIZEBORDER | FCF_SHELLPOSITION |
  285. FCR_TITLEBAR | FCF_SYSMENU |
  286. FCF_MINMAX | FCF_TASKLIST)];
  287.  
  288. [mainWindow makeKeyAndOrderFront: nil];
  289. [application run];
  290.  
  291. [mainWindow free];
  292. [application free];
  293. }
  294.  
  295. In addition to inititializing an application object, the main window is created 
  296. as an instance of MainWindow. The OS/2 window identifier is 1000, the window is 
  297. created with a resizable border. 
  298.  
  299. Calling the method -makeKeyAndOrderFront: shows the window. 
  300.  
  301. Figure~fig:test shows the window created by this simple piece of source code. 
  302.  
  303.  
  304. ΓòÉΓòÉΓòÉ 3.3. Necessary Include Files ΓòÉΓòÉΓòÉ
  305.  
  306. To use the OS/2 PM class library simply include the file <pm/pm.h> into your 
  307. application. This automatically includes all Objective C Interface Files and 
  308. the patched OS/2 API header file <objc/os2.h>. The patches are based on the 
  309. files \emx\include\os2.h from emx0.9b. 
  310.  
  311. When using the Database library, you have to include  <db/db.h>. To use any of 
  312. the supporiting classes found in the utility library, just include 
  313. <util/util.h>. 
  314.  
  315. After installing the libraries, these include files can be found in the 
  316. directories \usr\include\pm, \usr\include\db, \usr\include\util respectively 
  317. \usr\include\objc. 
  318.  
  319.  
  320. ΓòÉΓòÉΓòÉ 3.4. Compilation ΓòÉΓòÉΓòÉ
  321.  
  322. To compile programs using the PM class library just link the executable file 
  323. with the class library file, the utility library  and the Objective C runtime 
  324. library. The utility library is always needed. Some classes of either the PM 
  325. library and the database library will use classes of the utility library 
  326. (objcutil), this library therefore must be linked to all of your programs just 
  327. as the Objective C runtime library. 
  328.  
  329. The above examples (C and Objective C version) can be found in 
  330. \usr\samples\simple. To compile the C version, type 
  331.  
  332.      gcc -c simple_c.c ... to produce the object file simple_c.o. 
  333.      gcc -o simple_c.exe simple_c.o ... to produce the executable file 
  334.       simple_c.exe. 
  335.      emxbind -ep simple_c.exe ... to set the application type for simple_c.exe 
  336.       to OS/2 Presentation Manager Application. 
  337.  
  338.  To compile the---noticable shorter and simpler---Objective C version of the 
  339.  program, use 
  340.  
  341.      gcc -c simple_o.m ... to produce the object file simple_o.o. 
  342.      gcc -o simple_o.exe simple_o.o -lobjcpm -lobjcutil -lobjc ... to produce 
  343.       the executable application file  simple_o.exe. 
  344.      emxbind -ep simple_o.exe ... to set the application type for simple_o.exe 
  345.       to OS/2 Presentation Manager Application. 
  346.  
  347.  See Figure~fig:test for the output produced by the simple application. The 
  348.  look and feel of the window was determined by the FDS_xxxx flags specified at 
  349.  window creation (see the reference manual for more information concerning 
  350.  these flags). 
  351.  
  352.  After linking and setting the application type you can strip all debug symbols 
  353.  off the executable file by using the -s option of  emxbind. Using the command 
  354.  emxbind -s simple_c.exe respectively emxbind -s simple_o.exe will strip all 
  355.  debug information from the executable files. It must be stated that the 
  356.  Objective C version is significantly larger than the pure C version. In this 
  357.  simple case, the Objective C version simple_o.exe is about 15 times larger in 
  358.  size than simple_c.exe. This is caused by the large overhead of the statically 
  359.  linked libraries objcpm, objcutil and objc. The larger your programs get---no 
  360.  one will call such a small program a useful application---the difference in 
  361.  size between the Objective C and C versions will decrease. So, don't be afraid 
  362.  of creating too large applications. The library overhead---assuming statically 
  363.  linking the applications---will always be only about 150K. 
  364.  
  365.  Normally it's better to use a makefile for compiling and linking applications. 
  366.  A sample makefile is provided in \usr\samples\make. Just copy the two files 
  367.  makefile.preamble and makefile to your source code directory and fill in the 
  368.  blanks in makefile. For a description of how to do this, see section 
  369.  sec:makefile on page sec:makefile. 
  370.  
  371.  Later in this document it will be shown, how to simplify even these tasks of 
  372.  project management. The makefiles for your programs can be created for you by 
  373.  one of the graphical development tools, the Project Editor. 
  374.  
  375.  
  376. ΓòÉΓòÉΓòÉ 4. A Simple File-Browser ΓòÉΓòÉΓòÉ
  377.  
  378. This chapter describes a simple application, which already can provide quite 
  379. useful. Its purpose is to read a text file and display it in an OS/2 PM window. 
  380. The name of the text file is given as the first and only parameter at the 
  381. command line. The program itself will be called textview. 
  382.  
  383.  
  384.  
  385. "Textview" application displaying its own source code
  386.  
  387. To accomplish such a behaviour, one of the classes of the PM library---capable 
  388. of displaying various amounts of text by itself---will be used. Information on 
  389. the available classes in general, and on the Multi-Line entry field (MLE) used 
  390. here, can be found in the reference manual. 
  391.  
  392. The window should be resizable and its contents area (the MLE window) should 
  393. have the same size as the window itself. 
  394.  
  395. If you, for example, want to take a look at your main OS/2 configuration file, 
  396. just type textview c:\config.sys. The file will be loaded and displayed. 
  397.  
  398. Figure fig:textview shows the application main window displaying the source 
  399. code of the program itself. 
  400.  
  401.  
  402. ΓòÉΓòÉΓòÉ 4.1. Parts of the Program ΓòÉΓòÉΓòÉ
  403.  
  404. As shown before, the program consists of three parts, Initialization, the 
  405. Message loop and a Cleanup section. 
  406.  
  407.  
  408. ΓòÉΓòÉΓòÉ 4.1.1. Initialization ΓòÉΓòÉΓòÉ
  409.  
  410. The first section, Initialization, has to do the following: 
  411.  
  412.      Check for the command line parameters. There must be  exactly one 
  413.       parameter when starting the program, which is the name of the file to be 
  414.       displayed. 
  415.      Check, if the file exists; create a buffer area in memory with enough 
  416.       size to store the contents of the whole file. 
  417.      Read the data stored in the file into the buffer area. 
  418.      If all is o.k., create the application instance and a window. Insert a 
  419.       multi-line entry field into the window, where the text will be displayed. 
  420.      load the text buffer in memory to the display area of the multi-line 
  421.       entry field. It must be noted that only about 64KBytes of text can be 
  422.       copied to the MLE window at once. Using more data would require the data 
  423.       to be split in 64KByte blocks. As this is not performed in this simple 
  424.       application, the file size is limited to 64KBytes. 
  425.  
  426.  The first three sections of the initialization don't have anything to do with 
  427.  this class library. They only use functions of the EMX C-Library and are 
  428.  simple to understand: 
  429.  
  430.   main(int argc,char *argv[])
  431.   {
  432.   FILE *inputFile;
  433.   struct stat statbuffer;
  434.   char *contents;
  435.  
  436.   /*
  437.   * check for command line arguments and
  438.   * check given file (struct stat)
  439.   */
  440.   if (argc != 2) /* check for command line arguments,
  441.   must be exactly one */
  442.   exit (-1);
  443.  
  444.   if (stat (argv[1],&statbuffer) < 0) /* check file */
  445.   exit (-1);
  446.  
  447.   /*
  448.   * open file and read contents to buffer
  449.   */
  450.   inputFile = fopen (argv[1], r ); /* open text
  451.   file read-only */
  452.  
  453.   contents = (char *) malloc (statbuffer.st_size + 1);
  454.   /* allocate buffer */
  455.   fread (contents,statbuffer.st_size,1,inputFile);
  456.   /* read contents of file */
  457.  
  458.  inputFile is a pointer to a file structure returned by  fopen(). statbuffer is 
  459.  used to retrieve information about the file using the C-Library function 
  460.  stat(). Here the size of the file is stored. 
  461.  
  462.  After reading file information, contents is allocated via  malloc() and the 
  463.  file is opened and its contents are read to  contents. 
  464.  
  465.  For more documentation on the C library functions used here, either check a 
  466.  book concerning the programming language C and the standard library functions 
  467.  as available on most *nix systems, or consult the online reference manuals to 
  468.  the emx C library (normally found in \emx\book. 
  469.  
  470.  Following this part of the code, the initialization of the used PM classes 
  471.  takes place. 
  472.  
  473.  Just add some more variable declarations to the first section of the code: 
  474.  
  475.   StdApp *application;
  476.   StdWindow *window;
  477.   Window *mle;
  478.   char *title;
  479.  
  480.  title is used as a buffer area to store the title of the main window, where 
  481.  the text will be displayed, mle is a pointer to a generic window object, which 
  482.  will be initialized as a  MultiLineEntryField. application and window will 
  483.  hold pointers to the instances of the main application object and the main 
  484.  window respectively. 
  485.  
  486.  The initialization of these variable is performed as shown below: 
  487.  
  488.   /*
  489.   * create app instance and window,
  490.   * create MLE for text display
  491.   */
  492.   application = [[StdApp alloc] init]; /* initialize
  493.   application
  494.   object */
  495.   window = [[MainWindow alloc] initWithId: 1000
  496.   andFlags: (FCF_SIZEBORDER | FCF_TITLEBAR |
  497.   FCF_SYSMENU | FCF_MINMAX |
  498.   FCF_SHELLPOSITION | FCF_TASKLIST)];
  499.   /* create main window */
  500.  
  501.   [window createObjects]; /* create child windows
  502.   of main window */
  503.  
  504.   mle = [[MultiLineEntryField alloc]
  505.   initWithId: 1001
  506.   andFlags: (WS_VISIBLE | MLS_READONLY |
  507.   MLS_HSCROLL | MLS_VSCROLL)
  508.   in: window];
  509.   [window insertChild: mle]; /* insert MLE into window */
  510.  
  511.   /*
  512.   * calculate title of window and set it
  513.   */
  514.   title = (char *) malloc (11 + /* allocate buffer for title */
  515.   strlen (argv[1]));
  516.   sprintf (title, Textview:  ,argv[1]); /* fill title buffer */
  517.  
  518.   [window setTitle: title]; /* set window title */
  519.  
  520.   free (title); /* free title buffer */
  521.  
  522.  This section of code creates and initializes the application object and 
  523.  creates a main window with PM identifier 1000. 
  524.  
  525.  Afterwards all existing child objects of the window are created in memory 
  526.  using -createObjects. Then a PM MLE window is created (id 1001) and inserted 
  527.  into the main window. 
  528.  
  529.  The last part of the code simply allocates memory to hold the title string and 
  530.  creates the title string, which consists of the name of the application 
  531.  (Textview) and the name of the file to be displayed. 
  532.  
  533.  The MLE window is created in read-only mode with a horizontal and a vertical 
  534.  scrollbar (flags MLS_READONLY,  MLS_HSCROLL and MLS_VSCROLL). 
  535.  
  536.  After creating the objects and initializing the variables the main window is 
  537.  displayed and the size of the MLE window is adjusted to the size of the main 
  538.  window, to fill its complete interior: 
  539.  
  540.   /*
  541.   * show window, set MLE size and display contents of file
  542.   */
  543.   [window makeKeyAndOrderFront: nil]; /* show window */
  544.   [mle setSize: 0:0:[window width]:
  545.   [window height]]; /* set MLE size */
  546.   [mle setText: contents]; /* display contents of file */
  547.  
  548.  This piece of code also sets the text displayed in the MLE window to be the 
  549.  buffer area contents. 
  550.  
  551.  
  552. ΓòÉΓòÉΓòÉ 4.2. Message Loop ΓòÉΓòÉΓòÉ
  553.  
  554. The main message loop is started by calling [application run]. As mentioned 
  555. before, this method terminates, when the main window gets closed. 
  556.  
  557.  
  558. ΓòÉΓòÉΓòÉ 4.3. Cleanup ΓòÉΓòÉΓòÉ
  559.  
  560. After the window was closed, all objects are destroyed and the previously 
  561. allocated buffer area is freed again: 
  562.  
  563. /*
  564. * free all resources
  565. */
  566. free (contents); /* free contents buffer */
  567. fclose (inputFile); /* close file */
  568.  
  569. [application free]; /* free application */
  570. [window free]; /* free window */
  571.  
  572. Note, that [window free] automatically destroys all its child windows, in our 
  573. case, also the MLE window. 
  574.  
  575. If you encountered any problems in understanding the code presented 
  576. above---especially if the concepts of messaging in Objective C still seem to be 
  577. a bit obscure---consult one of the sources of information recommended at the 
  578. end of this document. 
  579.  
  580.  
  581. ΓòÉΓòÉΓòÉ 4.4. Compilation ΓòÉΓòÉΓòÉ
  582.  
  583. To compile this application, store the code shown in the following subsection 
  584. in the file textview.m (it can be found in \usr\samples\textview) and type: 
  585.  
  586.   gcc -c textview.m
  587.   gcc -o textview.exe textview.o -lobjcpm -lobjcutil -lobjc
  588.   emxbind -ep textview.exe
  589.  
  590. For comfort a makefile is provided in every directory containing sample code, 
  591. so you won't have to bother typing in the command lines starting compilation 
  592. and linking. 
  593.  
  594.  
  595. ΓòÉΓòÉΓòÉ 4.4.1. Complete Source Code of "textview.m" ΓòÉΓòÉΓòÉ
  596.  
  597. #include <pm/pm.h>
  598. #include <io.h>
  599. #include <sys/types.h>
  600. #include <sys/stat.h>
  601.  
  602. main(int argc,char *argv[])
  603. {
  604. StdApp *application;
  605. StdWindow *window;
  606. Window *mle;
  607. FILE *inputFile;
  608. struct stat statbuffer;
  609. char *contents;
  610. char *title;
  611.  
  612. /*
  613. * check for command line arguments and
  614. * check given file (struct stat)
  615. */
  616. if (argc != 2) /* check for command line arguments,
  617. must be exactly one */
  618. exit (-1);
  619.  
  620. if (stat (argv[1],&statbuffer) < 0) /* check file */
  621. exit (-1);
  622.  
  623. /*
  624. * open file and read contents to buffer
  625. */
  626. inputFile = fopen (argv[1], r ); /* open text
  627. file read-only */
  628.  
  629. contents = (char *) malloc (statbuffer.st_size + 1);
  630. /* allocate buffer */
  631. fread (contents,statbuffer.st_size,1,inputFile);
  632. /* read contents of file */
  633.  
  634. /*
  635. * create app instance and window,
  636. * create MLE for text display
  637. */
  638. application = [[StdApp alloc] init]; /* initialize
  639. application
  640. object */
  641. window = [[MainWindow alloc] initWithId: 1000
  642. andFlags: (FCF_SIZEBORDER | FCF_TITLEBAR |
  643. FCF_SYSMENU | FCF_MINMAX |
  644. FCF_SHELLPOSITION | FCF_TASKLIST)];
  645. /* create main window */
  646.  
  647. [window createObjects]; /* create child windows
  648. of main window */
  649.  
  650. mle = [[MultiLineEntryField alloc]
  651. initWithId: 1001
  652. andFlags: (WS_VISIBLE | MLS_READONLY |
  653. MLS_HSCROLL | MLS_VSCROLL)
  654. in: window];
  655. [window insertChild: mle]; /* insert MLE into window */
  656.  
  657. /*
  658. * calculate title of window and set it
  659. */
  660. title = (char *) malloc (11 + /* allocate buffer for title */
  661. strlen (argv[1]));
  662. sprintf (title, Textview:  ,argv[1]); /* fill title buffer */
  663.  
  664. [window setTitle: title]; /* set window title */
  665.  
  666. free (title); /* free title buffer */
  667.  
  668. /*
  669. * show window, set MLE size and display contents of file
  670. */
  671. [window makeKeyAndOrderFront: nil]; /* show window */
  672. [mle setSize: 0:0:[window width]:
  673. [window height]]; /* set MLE size */
  674. [mle setText: contents]; /* display contents of file */
  675.  
  676. /*
  677. * run application
  678. */
  679. [application run];
  680.  
  681. /*
  682. * free all resources
  683. */
  684. free (contents); /* free contents buffer */
  685. fclose (inputFile); /* close file */
  686.  
  687. [application free]; /* free application */
  688. [window free]; /* free window */
  689. }
  690.  
  691. If you compile and link this program (just type make in the appropriate 
  692. directory) you will see, that although the main window is resizable, the MLE 
  693. window inside the window remains the same size, no matter what size its parent 
  694. window is of. 
  695.  
  696. The rest of this chapter will show how an object can be automatically notified, 
  697. e.g. when the main window resizes, to be able to adapt the size of the MLE 
  698. window as expected by the user of the application. 
  699.  
  700. Always keep in mind, that only a little fraction of the classes services are 
  701. mentioned here. For full information read the appropriate sections on every 
  702. class in the reference manual. Instances of MainWindow will not only notify an 
  703. object of a change in its size, but also of, e.g., an action causing the window 
  704. being moved, closed, etc. 
  705.  
  706.  
  707. ΓòÉΓòÉΓòÉ 4.5. Delegate Objects ΓòÉΓòÉΓòÉ
  708.  
  709. One of the major advantages of the Objective C language when compared to most 
  710. other object-oriented programming languages is the possibility to check at 
  711. runtime, if an object implements a specific method or if it does not. This 
  712. provides a simple way for objects to send messages to other objects, if these 
  713. messages can be processed, whenever an object thinks another object has to be 
  714. notified of some special occurrence. 
  715.  
  716. An object implementing methods called by another object, to be notified of some 
  717. special events, is called a delegate object. 
  718.  
  719. So it is possible to create classes, and thereafter objects of these classes, 
  720. which can change one predefined classes behaviour without the need of 
  721. subclassing one of the predefined classes. 
  722.  
  723. Delegation is used by some objects in this library---mostly by window objects 
  724. at the time. The information on which specific delegate methods are supported 
  725. by an object of some class can be found in the reference manual. In the 
  726. documentation for every class supporting delegate methods---if this is not only 
  727. inherited behaviour from its superclass---you can find a section called Methods 
  728. implemented by the delegate. Any of these methods is called automatically by an 
  729. object if the delegate implements this special method and there is need to 
  730. inform another object of some special occurence, e.g. when a window is resized 
  731. or closed. 
  732.  
  733. Using the method -setDelegate: you can assign a special object, implementing 
  734. some delegate functions, as the delegate object of an instance of any window 
  735. class, for example to an instance of  MainWindow or StdDialog. 
  736.  
  737. If the delegate object implements any of the methods described in the section 
  738. Methods implemented by the delegate which is part of some class descriptions in 
  739. the reference part of this manual, these methods get called at the occurrences 
  740. described there. 
  741.  
  742. For our purposes, we will use the delegate method -windowDidResize:, which is 
  743. called whenever the window gets resized by the user or the application program. 
  744.  
  745. This method will then query the size of the sending instance of StdWindow and 
  746. accustom the size of the MLE window according to this. 
  747.  
  748.  
  749. ΓòÉΓòÉΓòÉ 4.6. Implementing the Delegate ΓòÉΓòÉΓòÉ
  750.  
  751. First we have to define a new class, implementing the method -windowDidResize:. 
  752. The class declaration is quite simple: 
  753.  
  754. @interface Controller : Object
  755. {
  756. }
  757.  
  758. - windowDidResize: sender;
  759.  
  760. @end
  761.  
  762. This declaration defines a new class, a subclass of Object, called Controller, 
  763. which has no new instance variables but those inherited from its superclass and 
  764. implements one method called  -windowDidResize:. 
  765.  
  766. The implementation of this simple class looks like this: 
  767.  
  768. @implementation Controller
  769.  
  770. - windowDidResize: sender
  771. {
  772. [[sender findFromID: 1001] setSize:
  773. 0:0:[sender width]:[sender height]];
  774. return self;
  775. }
  776.  
  777. @end
  778.  
  779. This is a simple method, just calling some methods of sender and of the 
  780. previously created MLE window. 
  781.  
  782. By calling [sender findFromID: 1001] the method queries a pointer to an 
  783. instance of Window or one of its subclasses. This window must be a child window 
  784. of sender and have the OS/2 PM identifier 1001. 
  785.  
  786. This method returns a pointer to the MLE window's associated Window object. 
  787. This method is sent a -setSize:::: message to adapt its size to the size of the 
  788. sending window. 
  789.  
  790. -setSize:::: takes the coordinates of the lower left corner of the window (the 
  791. first and second parameter) relative to its parent's lower left corner. The 
  792. last two parameters represent the width and height the window should be resized 
  793. to. 
  794.  
  795. The lower left corner of the MLE window should be the same as the lower left 
  796. corner of its parent, (0/0). The width and height of the MLE window is queried 
  797. from the sender by using the appropriate methods -width and -height. 
  798.  
  799. As this method has a return type of id (id is a pointer to a generic Objective 
  800. C object), self is returned on successful execution of the method. 
  801.  
  802. The following section shows the modified source code of textview.m, which is 
  803. stored in the directory \usr\samples\textview under the name textview2.m. 
  804.  
  805.  
  806. ΓòÉΓòÉΓòÉ 4.6.1. Modified version of Textview: "textview2.m" ΓòÉΓòÉΓòÉ
  807.  
  808. #include <pm/pm.h>
  809. #include <io.h>
  810. #include <sys/types.h>
  811. #include <sys/stat.h>
  812.  
  813. @interface Controller : Object
  814. {
  815. }
  816.  
  817. - windowDidResize: sender;
  818.  
  819. @end
  820.  
  821. @implementation Controller
  822.  
  823. - windowDidResize: sender
  824. {
  825. [[sender findFromID: 1001] setSize:
  826. 0:0:[sender width]:[sender height]];
  827. return self;
  828. }
  829.  
  830. @end
  831.  
  832. main(int argc,char *argv[])
  833. {
  834. StdApp *application;
  835. StdWindow *window;
  836. Window *mle;
  837. Controller *controller;
  838. FILE *inputFile;
  839. struct stat statbuffer;
  840. char *contents;
  841. char *title;
  842.  
  843. /*
  844. * check for command line arguments
  845. * and check given file (struct stat)
  846. */
  847. if (argc != 2) /* check for command line arguments,
  848. must be exactly one */
  849. exit (-1);
  850.  
  851. if (stat (argv[1],&statbuffer) < 0) /* check file */
  852. exit (-1);
  853.  
  854. /*
  855. * open file and read contents to buffer
  856. */
  857. inputFile = fopen (argv[1], r ); /* open text file read-only */
  858.  
  859. contents = (char *) malloc (statbuffer.st_size + 1);
  860. /* allocate buffer */
  861. fread (contents,statbuffer.st_size,1,inputFile);
  862. /* read contents of file */
  863.  
  864. /*
  865. * create app instance and window, create MLE for text display
  866. */
  867. application = [[StdApp alloc] init]; /* initialize application
  868. object */
  869. window = [[MainWindow alloc] initWithId: 1000
  870. andFlags: (FCF_SIZEBORDER | FCF_TITLEBAR |
  871. FCF_SYSMENU | FCF_MINMAX |
  872. FCF_SHELLPOSITION | FCF_TASKLIST)];
  873. /* create main window */
  874. controller = [[Controller alloc] init];
  875.  
  876. [window createObjects]; /* create child windows of
  877. main window */
  878. [window setDelegate: controller];
  879.  
  880. mle = [[MultiLineEntryField alloc]
  881. initWithId: 1001
  882. andFlags: (WS_VISIBLE | MLS_READONLY |
  883. MLS_HSCROLL | MLS_VSCROLL)
  884. in: window];
  885. [window insertChild: mle]; /* insert MLE into window */
  886.  
  887. /*
  888. * calculate title of window and set it
  889. */
  890. title = (char *) malloc (11 + /* allocate buffer for title */
  891. strlen (argv[1]));
  892. sprintf (title, Textview:  ,argv[1]); /* fill title buffer */
  893.  
  894. [window setTitle: title]; /* set window title */
  895.  
  896. free (title); /* free title buffer */
  897.  
  898. /*
  899. * show window and display contents of file
  900. */
  901. [mle setText: contents]; /* display contents of file */
  902. [window makeKeyAndOrderFront: nil]; /* show window */
  903.  
  904. /*
  905. * run application
  906. */
  907. [application run];
  908.  
  909. /*
  910. * free all resources
  911. */
  912. free (contents); /* free contents buffer */
  913. fclose (inputFile); /* close file */
  914.  
  915. [application free]; /* free application */
  916. [window free]; /* free window */
  917. [controller free]; /* free controller */
  918. }
  919.  
  920.  
  921. ΓòÉΓòÉΓòÉ 4.7. Sample Makefiles ΓòÉΓòÉΓòÉ
  922.  
  923. In the directory \usr\samples\makefile you can find a sample makefile together 
  924. with the associated include file  makefile.preamble. 
  925.  
  926. To use this makefile, just copy makefile and  makefile.preamble to your 
  927. application directory and fill in the correct places in makefile. 
  928.  
  929.    1. Add the name of your application file to the line containing APPLICATION 
  930.       = (including the suffix .exe). 
  931.    2. Add the names of your object files to the line containing  OBJECTS =. 
  932.    3. Add all OS/2 resource files (the files with extenstion  .res) to the line 
  933.       containing the statement RESOURCES =. 
  934.  
  935.  This makefile was written for GNU make. Possible targets are: 
  936.  
  937.      no target ... this automatically compiles and links the application 
  938.       program 
  939.      dep or depend ... check all files for dependencies and create a .depend 
  940.       file, which is automatically included. 
  941.      clean ... removes all temporary files (compiled resources, application 
  942.       program, object files, core dump file, ...) 
  943.  
  944.   # Makefile for PM programs using Objective C class library
  945.  
  946.   include Makefile.preamble
  947.  
  948.   ifeq (.depend,(wildcard .depend))
  949.   include .depend
  950.   endif
  951.  
  952.   APPLICATION =
  953.   OBJECTS =
  954.   RESOURCES =
  955.  
  956.   all: (APPLICATION)
  957.  
  958.   depend dep:
  959.   (CPP) -MM *.m > .depend
  960.  
  961.   (APPLICATION): (OBJECTS) (RESOURCES)
  962.   (CC) -o (APPLICATION) (OBJECTS) (RESOURCES) \
  963.   -lobjcpm -lobjc
  964.   emxbind -ep (APPLICATION)
  965.   (STRIP) (APPLICATION)
  966.  
  967.   clean:
  968.   rm -rf (OBJECTS) (RESOURCES) (APPLICATION) core *~
  969.  
  970.  If you plan to write applications from scratch, these makefiles can be of some 
  971.  value to you, sparing you the time of writing one every time you start a 
  972.  project. Later on the development tools shipped together with this libraries 
  973.  will be introduced, showing how to visually build an application, design its 
  974.  user interface and compile and link with a single mouse-click. 
  975.  
  976.  
  977. ΓòÉΓòÉΓòÉ 5. Loading Resources ΓòÉΓòÉΓòÉ
  978.  
  979. Using the OS/2 Resource Compiler RC.EXE, you can create a  binary resource file 
  980. from a resource definition file. This binary resource file can be linked to 
  981. your application main module just like normal object files. The application can 
  982. then load some of the resource templates instead of creating dialog windows, 
  983. menus or many other window objects from scratch by creating and inserting 
  984. window objects into a parent window. 
  985.  
  986. In contrast to previous versions of this product using resource definition 
  987. files is only needed to define menus---as well for "normal" pull-down menus for 
  988. windows and dialog windows as for popup menus---and bitmap/icon resources. The 
  989. preferred way of creating other user interface elements, just as dialog or main 
  990. windows, is to use the Interface Editor program for visual development. 
  991. Resource files should only be used for those elements currently not supported 
  992. by the Interface Editor. 
  993.  
  994.  
  995. ΓòÉΓòÉΓòÉ 5.1. Adding a Menu Resource to Textview ΓòÉΓòÉΓòÉ
  996.  
  997. Just for demonstration issues, I'd like to show how to add a simple menu 
  998. resource to the main window (the only window) of the previously described 
  999. Textview application. 
  1000.  
  1001. Only one menu shall be added to Textview, a menu called File, which just 
  1002. includes the following menu items: 
  1003.  
  1004.      Open... ... to open and display a textfile 
  1005.      Exit ... to close the application window and exit 
  1006.  
  1007.  
  1008.  
  1009.   Simple menu for "Textview"
  1010.  
  1011.  The definition of these menu items are as follows: 
  1012.  
  1013.   MENU 1000
  1014.   {
  1015.   SUBMENU ~File , 2000
  1016.   {
  1017.   MENUITEM Open... , 2001
  1018.   MENUITEM SEPARATOR
  1019.   MENUITEM Exit , 2002
  1020.   }
  1021.   }
  1022.  
  1023.  The menu File has id 2000, the menu items Open... and Exit the ids 2001 
  1024.  respectively 2002. 
  1025.  
  1026.  Between the two menu items Open... and Exit a separator item should be 
  1027.  inserted. 
  1028.  
  1029.  The resulting menu is shown in figure fig:menutext. 
  1030.  
  1031.  To load this menu, just create a resource definition file, type in the menu 
  1032.  declaration and use RC.EXE to produce a binary resource file. When linking the 
  1033.  application, do not forget to specify the name of the binary resource file 
  1034.  (just like any other object file). 
  1035.  
  1036.  Note that one of the reasons your application will not behave as expected, 
  1037.  e.g. nothing happens when starting the program although it seems to be 
  1038.  running, can be that you just forgot to add the resource file to the linker 
  1039.  command line. Any program creating a window and thereby specifying some 
  1040.  resource flags, e.g. FCF_MENU, and the resource object could not be found in 
  1041.  the executable file, the program just stops and waits, and waits~ When 
  1042.  creating the main window of Textview, add FCF_MENU to the given flags 
  1043.  (FCF_SIZEBORDER, etc.). When creating the window, the menu resource will be 
  1044.  loaded and displayed in the window's actionbar. Which menu will be loaded 
  1045.  depends on the OS/2 PM identifier of the window, which you specify at window 
  1046.  creation. It must be the same as the identifier specified in the resource 
  1047.  definition for the menu (in our case, it is 1000). 
  1048.  
  1049.  For detailed information on the syntax of resource script files see the online 
  1050.  documentation supplied with the IBM OS/2 developer's toolkit (or some other 
  1051.  source of information, e.g. the OS/2 Redbooks). 
  1052.  
  1053.  
  1054. ΓòÉΓòÉΓòÉ 5.2. Dialogs ΓòÉΓòÉΓòÉ
  1055.  
  1056. Using a dialog editor, you can easily create dialog windows and either store a 
  1057. resource definition file or a binary resource file to disk. 
  1058.  
  1059. Just like normal windows, dialog windows are created by the application using 
  1060. the appropriate dialog window class StdDialog. In addition to creating the 
  1061. window object, the contents of the dialog are loaded from the main resource 
  1062. file linked to the application. 
  1063.  
  1064. After creation, dialog windows can be displayed using -makeKeyAndOrderFront:. 
  1065. In addition to normally displaying the dialog windows, which causes the dialog 
  1066. to run non-modal, you can also run a dialog modal for a given parent window. 
  1067. Using -runModalFor: the dialog window is displayed, but working with its parent 
  1068. window, which it runs modal for, is not possible until the dialog window gets 
  1069. closed again (dismissed). 
  1070.  
  1071.  
  1072. ΓòÉΓòÉΓòÉ 5.3. Command Bindings ΓòÉΓòÉΓòÉ
  1073.  
  1074. After a menu bar has been created, or a dialog window was loaded from a 
  1075. resource file, some of the menu items or window objects in the dialog send 
  1076. command messages to their owner. By processing these messages, the program can 
  1077. react to user actions. 
  1078.  
  1079. Using the classes provided by this library, you can bind command messages to 
  1080. designated methods of an object. When a special command message was sent to a 
  1081. window, the appropriate method of an object gets called. 
  1082.  
  1083.  
  1084. ΓòÉΓòÉΓòÉ 5.3.1. Outlets and Actions ΓòÉΓòÉΓòÉ
  1085.  
  1086. All methods which can be bound to command messages must be of the form 
  1087. -nameOfMethod: sender. The parameter sender stores a pointer to the sending 
  1088. instance of a StdWindow or a StdDialog, which calls the method. 
  1089.  
  1090. In the following methods (messages) of this style are called actions if they 
  1091. are intended to be called from some other object as a reaction to some user 
  1092. action. In addition to this special kind of method, there are also some special 
  1093. instance variables. Every instance variable of type id which is intended to 
  1094. just store a pointer to another object---normally a delegate object of some 
  1095. kind---is called an outlet. Take care to remember these two terms, they will be 
  1096. the basis the visual development supported by the Interface Editor is built on. 
  1097.  
  1098. Command messages can be bound to objects and appropriate methods using 
  1099. -bindCommand: withObject: selector:. The first parameter of this method is the 
  1100. identifier of the PM object, which posts command messages, the second is a 
  1101. pointer to the Objective C object, which implements the method to be called, 
  1102. the third and last is the selector of the method to be called. The selector of 
  1103. a method can be queried using @selector(nameOfMethod). 
  1104.  
  1105. To bind the command message sent by the menu item Exit, which has an OS/2 PM id 
  1106. of 2002 to the -performClose: method of the window object, just insert 
  1107.  
  1108. [window bindCommand: 2002
  1109. withObject: window
  1110. andSelector: @selector(performClose:)];
  1111.  
  1112. into the source code of textview before the -makeKeyAndOrderFront: statement. 
  1113.  
  1114. This results in calling [window performClose: window] whenever the menu item 
  1115. Exit gets selected by the user. 
  1116.  
  1117.  
  1118. ΓòÉΓòÉΓòÉ 5.4. An Application using a dialog and command bindings ΓòÉΓòÉΓòÉ
  1119.  
  1120. To demonstrate how to use and load dialog windows from a binary resource file 
  1121. and command bindings, let's look at a simple application providing a (very 
  1122. limitated) interface to the powerful plotting program Gnuplot. 
  1123.  
  1124. The backend (gnuplot.exe) is assumed to be installed somewhere in the program 
  1125. search path. This interface doesn't check, if the program could be successfully 
  1126. found and started. 
  1127.  
  1128.  
  1129.  
  1130. Simple PM interface to "Gnuplot"
  1131.  
  1132. The program itself only consists of a dialog, which is displayed when starting 
  1133. the program. This dialog contains three entry fields, a checkbox and a 
  1134. pushbutton. 
  1135.  
  1136. The first entry field is used to specify, which function to plot, the other two 
  1137. to specify the horizontal plotting range. The plotting range is only used, when 
  1138. the checkbox is in checked state. After pressing the pushbutton Plot, the entry 
  1139. fields and the checkbox are computed and the function is plotted. 
  1140.  
  1141. Figure~fig:gnuplot shows what the dialog looks like. 
  1142.  
  1143. The main implementation file called plot.m is really simple. It just creates 
  1144. the necessary instances of StdApp and  StdDialog. In addition to this a 
  1145. controller object is instantiated which reads and interprets the data from the 
  1146. entry fields and performs the plotting. 
  1147.  
  1148. After creating all objects, a command binding is set up for the pushbutton Plot 
  1149. with the method -plot: of controller. 
  1150.  
  1151. Then the dialog is shown and run modal and afterwards all previously allocated 
  1152. objects get freed again. 
  1153.  
  1154.  
  1155. ΓòÉΓòÉΓòÉ 5.4.1. "plot.m", the Main Implementation ΓòÉΓòÉΓòÉ
  1156.  
  1157. #include <pm/pm.h>
  1158.  
  1159. #include gnuplot.h
  1160. #include controller.h
  1161.  
  1162. main()
  1163. {
  1164. StdApp *application = [[StdApp alloc] init];
  1165. StdDialog *mainDialog = [[StdDialog alloc]
  1166. initWithId: IDD_MAIN];
  1167. Controller *controller = [[Controller alloc] init];
  1168.  
  1169. [mainDialog createObjects];
  1170.  
  1171. [mainDialog bindCommand: DID_OK withObject: controller
  1172. selector: @selector(plot:)];
  1173.  
  1174. [mainDialog runModalFor: nil];
  1175.  
  1176. [controller free];
  1177. [mainDialog free];
  1178. [application free];
  1179. }
  1180.  
  1181. [[StdDialog alloc] initWithId: IDD_MAIN] creates a dialog object and loads its 
  1182. binary resource template from the main binary resource file. The dialog id is 
  1183. IDD_MAIN. 
  1184.  
  1185. [mainDialog bindCommand: ...] binds the command message sent by the pushbutton, 
  1186. which has id DID_OK to the -plot: method of the object controller. 
  1187.  
  1188. [mainDialog runModalFor: nil] runs a modal dialog. Normally, this dialog is run 
  1189. modal for a certain window, but when nil is specified, this only causes the 
  1190. method to wait for termination of the dialog window. 
  1191.  
  1192. The class Controller itself has to load the program gnuplot.exe and send it 
  1193. appropriate commands to plot the given function. 
  1194.  
  1195. The class implements one instance variable, gnuplot to store a file handle to 
  1196. the gnuplot program, and three methods, -init to open the plotting program, 
  1197. -free to close it at the end and -plot:, which does the plotting work. The 
  1198. following interface declarations is stored as controller.h in 
  1199. \usr\samples\gnuplot. 
  1200.  
  1201.  
  1202. ΓòÉΓòÉΓòÉ 5.4.2. "controller.h", Gnuplot PM Interface ΓòÉΓòÉΓòÉ
  1203.  
  1204. #include <pm/pm.h>
  1205. #include <stdio.h>
  1206.  
  1207. @interface Controller : Object
  1208. {
  1209. FILE *gnuplot;
  1210. }
  1211.  
  1212. - init;
  1213. - free;
  1214. - plot: sender;
  1215.  
  1216. @end
  1217.  
  1218. The implementation uses some of the unix-like features of the emx C-Library. 
  1219.  
  1220. - init
  1221. {
  1222. [super init];
  1223. gnuplot = popen ( gnuplot.exe , w );
  1224. return self;
  1225. }
  1226.  
  1227. -init first initializes its superclass Object and thereafter opens a pipe for 
  1228. writing to the plotting program gnuplot.exe. This binds stdin of gnuplot.exe to 
  1229. the pipe, which is represented as the file structure stored in the instance 
  1230. variable gnuplot. 
  1231.  
  1232. - free
  1233. {
  1234. pclose (gnuplot);
  1235. return [super free];
  1236. }
  1237.  
  1238. -free just closes the pipe and frees its instance by calling the -free method 
  1239. of its superclass. 
  1240.  
  1241. The following source code for the method -plot: is a bit more complicated. 
  1242. Using the -findFromID: method of sender, pointers to the entry field and 
  1243. checkbox objects are found out. 
  1244.  
  1245. The function to be plot is stored in text, the left and right range boundaries 
  1246. are stored in leftX and rightX. 
  1247.  
  1248. If the checkbox is checked, the left and right boundaries are read and 
  1249. converted to double numbers. Then gnuplot is sent the appropriate plot string 
  1250. used to plot a function in a given horizontal range. 
  1251.  
  1252. If the checkbox is unchecked or one of the boundaries is not valid, gnuplot is 
  1253. sent a normal string to plot the function without specifying a plot range. 
  1254.  
  1255. - plot: sender
  1256. {
  1257. char *string;
  1258. char *leftX,*rightX;
  1259. double left,right;
  1260.  
  1261. string = [[sender findFromID: IDD_PLOTSTRING] text: NULL];
  1262.  
  1263. if ([[sender findFromID: IDD_RANGECHECK] checked]) {
  1264. leftX = [[sender findFromID: IDD_LEFTX] text: NULL];
  1265. rightX = [[sender findFromID: IDD_RIGHTX] text: NULL];
  1266.  
  1267. if ((sscanf (leftX,  ,&left) == 1) &&
  1268. (sscanf (rightX,  ,&right) == 1) &&
  1269. (right > left)) {
  1270. fprintf (gnuplot, plot [ ,left,right,string);
  1271. } else
  1272. fprintf (gnuplot, plot  ,string);
  1273.  
  1274. free (leftX);
  1275. free (rightX);
  1276. } else
  1277. fprintf (gnuplot, plot  ,string);
  1278.  
  1279. fflush (gnuplot);
  1280. free (string);
  1281.  
  1282. return self;
  1283. }
  1284.  
  1285. The following section shows the complete source code of the implementation of 
  1286. the class Controller. 
  1287.  
  1288.  
  1289. ΓòÉΓòÉΓòÉ 5.4.3. "controller.m", Gnuplot PM Interface ΓòÉΓòÉΓòÉ
  1290.  
  1291. #include Controller.h
  1292. #include gnuplot.h
  1293.  
  1294. @implementation Controller
  1295.  
  1296. - init
  1297. {
  1298. [super init];
  1299. gnuplot = popen ( gnuplot.exe , w );
  1300. return self;
  1301. }
  1302.  
  1303. - free
  1304. {
  1305. pclose (gnuplot);
  1306. return [super free];
  1307. }
  1308.  
  1309. - plot: sender
  1310. {
  1311. char *string;
  1312. char *leftX,*rightX;
  1313. double left,right;
  1314.  
  1315. string = [[sender findFromID: IDD_PLOTSTRING] text: NULL];
  1316.  
  1317. if ([[sender findFromID: IDD_RANGECHECK] checked]) {
  1318. leftX = [[sender findFromID: IDD_LEFTX] text: NULL];
  1319. rightX = [[sender findFromID: IDD_RIGHTX] text: NULL];
  1320.  
  1321. if ((sscanf (leftX,  ,&left) == 1) &&
  1322. (sscanf (rightX,  ,&right) == 1) &&
  1323. (right > left)) {
  1324. fprintf (gnuplot, plot [ ,left,right,string);
  1325. } else
  1326. fprintf (gnuplot, plot  ,string);
  1327.  
  1328. free (leftX);
  1329. free (rightX);
  1330. } else
  1331. fprintf (gnuplot, plot  ,string);
  1332.  
  1333. fflush (gnuplot);
  1334. free (string);
  1335.  
  1336. return self;
  1337. }
  1338.  
  1339. @end
  1340.  
  1341.  
  1342. ΓòÉΓòÉΓòÉ 5.4.4. Resource Definition ΓòÉΓòÉΓòÉ
  1343.  
  1344. The resource definition consists of three files, the main resource definition 
  1345. file, which only includes the dialog template definition. The dialog template 
  1346. definition file defines the main dialog; and the header file to declare all 
  1347. constants used by the dialog definition. 
  1348.  
  1349. #define INCL_PM
  1350. #define INCL_NLS
  1351.  
  1352. #include <os2.h>
  1353. #include gnuplot.h
  1354.  
  1355. rcinclude gnuplot.dlg
  1356.  
  1357. The file depicted above is stored as gnuplot.rc. It only includes the files 
  1358. os2.h and gnuplot.h, which are the headerfiles used for the resource 
  1359. definition, and afterwards includes the dialog definition file gnuplot.dlg. In 
  1360. contrast to the Objective C source code files, the file <os2.h> is included 
  1361. instead of <objc/os2.h>. The reason for this is that only Objective C programs 
  1362. need a special version of this include file because some of the data types and 
  1363. definitions made in <os2.h> are not compatible with some of the Objective C 
  1364. datatypes and definitions. 
  1365.  
  1366. DLGTEMPLATE IDD_MAIN LOADONCALL MOVEABLE DISCARDABLE
  1367. {
  1368. DIALOG GNUPLOT Interface ,
  1369. IDD_MAIN, 158, 90, 210, 65,
  1370. FS_NOBYTEALIGN | FS_DLGBORDER |
  1371. FS_SCREENALIGN | NOT WS_VISIBLE |
  1372. WS_CLIPSIBLINGS | WS_SAVEBITS,
  1373. FCF_TITLEBAR | FCF_SYSMENU | FCF_NOBYTEALIGN
  1374. {
  1375. CONTROL ,
  1376. IDD_PLOTSTRING, 60, 43, 127, 8, WC_ENTRYFIELD,
  1377. ES_MARGIN | ES_AUTOSCROLL | WS_TABSTOP | WS_VISIBLE
  1378. CTLDATA 8, 32, 0, 0
  1379. CONTROL Function: ,
  1380. 0, 15, 43, 40, 8, WC_STATIC,
  1381. SS_TEXT | DT_LEFT | DT_TOP | DT_MNEMONIC | WS_GROUP |
  1382. WS_VISIBLE
  1383. CONTROL Range: ,
  1384. 0, 15, 30, 40, 8, WC_STATIC,
  1385. SS_TEXT | DT_LEFT | DT_TOP | DT_MNEMONIC | WS_GROUP |
  1386. WS_VISIBLE
  1387. CONTROL ,
  1388. IDD_LEFTX, 60, 30, 50, 8, WC_ENTRYFIELD,
  1389. ES_MARGIN | ES_AUTOSCROLL | WS_TABSTOP | WS_VISIBLE
  1390. CTLDATA 8, 8, 0, 0
  1391. CONTROL ,
  1392. IDD_RIGHTX, 120, 30, 50, 8, WC_ENTRYFIELD,
  1393. ES_MARGIN | ES_AUTOSCROLL | WS_TABSTOP | WS_VISIBLE
  1394. CTLDATA 8, 8, 0, 0
  1395. CONTROL ,
  1396. IDD_RANGECHECK, 179, 30, 10, 10, WC_BUTTON,
  1397. BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE
  1398. CONTROL Plot ,
  1399. DID_OK, 145, 10, 40, 14, WC_BUTTON,
  1400. BS_PUSHBUTTON | BS_DEFAULT | WS_TABSTOP | WS_VISIBLE
  1401. }
  1402. }
  1403.  
  1404. gnuplot.dlg defines a dialog template for a dialog window having the PM 
  1405. identifier IDD_MAIN. This resource template is normally created by a dialog 
  1406. editor. Using resource scripts is not the preferred way to store user interface 
  1407. data when using the Objective C class libraries. Instead you should create user 
  1408. interface objects---as far as it is possible at the moment---using the 
  1409. Interface Editor application also found in this package. See 
  1410. Chapter~cha:ibintro and the following chapters and the Application Development 
  1411. Tools manual for an introduction into this matter. 
  1412.  
  1413. #define IDD_MAIN 3000
  1414. #define IDD_PLOTSTRING 3001
  1415. #define IDD_PLOT 3002
  1416. #define IDD_LEFTX 3003
  1417. #define IDD_RIGHTX 3004
  1418. #define IDD_RANGECHECK 3005
  1419.  
  1420. The include file gnuplot.h normally is also created by the dialog editor. It 
  1421. contains definitions for the constants used in the resource definition file. 
  1422.  
  1423. The binary resource file can be created using RC.EXE by typing the command 
  1424. sequence rc -r gnuplot.rc at an OS/2 command line. This creates the binary 
  1425. resource file gnuplot.res, which can be linked to the application as the main 
  1426. resource file. 
  1427.  
  1428. Compare the following makefile to the makefile template described in section 
  1429. sec:makefile at page sec:makefile to realize, how to fill in the blanks to 
  1430. create a makefile specific for your applications. 
  1431.  
  1432. # Makefile for PM programs using Objective C class library
  1433.  
  1434. include Makefile.preamble
  1435.  
  1436. ifeq (.depend,(wildcard .depend))
  1437. include .depend
  1438. endif
  1439.  
  1440. APPLICATION = plot.exe
  1441. OBJECTS = plot.o controller.o
  1442. RESOURCES = gnuplot.res
  1443.  
  1444. all: (APPLICATION)
  1445.  
  1446. depend dep:
  1447. (CPP) -MM *.m > .depend
  1448.  
  1449. (APPLICATION): (OBJECTS) (RESOURCES)
  1450. (CC) -o (APPLICATION) (OBJECTS) (RESOURCES) \
  1451. -lobjcpm -lobjc
  1452. emxbind -ep (APPLICATION)
  1453. (STRIP) (APPLICATION)
  1454.  
  1455. clean:
  1456. rm -rf (OBJECTS) (RESOURCES) (APPLICATION) core *~
  1457.  
  1458.  
  1459. ΓòÉΓòÉΓòÉ 6. Using the Database Library ΓòÉΓòÉΓòÉ
  1460.  
  1461. In contrast to the previous chapters this chapter will not deal with writing 
  1462. Presentation Manager programs. Instead of this a short overview of the database 
  1463. library is presented. To simplify things the first application making use of 
  1464. the database features will have no PM interface. The later chapters will 
  1465. describe how to make use of both libraries---for PM and for database 
  1466. programming---in one application program. 
  1467.  
  1468.  
  1469. ΓòÉΓòÉΓòÉ 6.1. Preparations ΓòÉΓòÉΓòÉ
  1470.  
  1471. After installing the library package, the include files for the database 
  1472. library can be found in \usr\include\db. Modules which use some of the classes 
  1473. provided, simply have to include the file <db/db.h>. Just as with <pm/pm.h>, 
  1474. this single include file includes all necessary files to provide full access to 
  1475. all classes and methods. 
  1476.  
  1477. After compilation the program must be linked with objcdb.a, the library file, 
  1478. and again with the utility classes library objcutil.a. This can be accomplished 
  1479. by specifying  -lobjcdb and -lobjcutil when linking the program with  gcc. 
  1480.  
  1481.  
  1482. ΓòÉΓòÉΓòÉ 6.2. Accessing a DBase III File ΓòÉΓòÉΓòÉ
  1483.  
  1484. As was already mentioned before, the database library provides read and write 
  1485. access to DBase III data files. At the moment,  Memo-fields are not supported. 
  1486.  
  1487. There's also no way to use indexing or sorting now. As this restricts the 
  1488. usability of the library to small database files, indexing is worked on at the 
  1489. moment. 
  1490.  
  1491. The basic class for accessing DBase III files is DBFile. When allocating and 
  1492. initializing an object of this class, an existing data file is opened for 
  1493. reading and writing. This implies, that concurrent access to the same database 
  1494. files is not provided. So be careful not to write programs accessing the same 
  1495. database files at the same time. 
  1496.  
  1497. A main function of a C program using database files would look like that: 
  1498.  
  1499. main ()
  1500. {
  1501. DBFile *myDBFile;
  1502.  
  1503. .
  1504. .
  1505. .
  1506.  
  1507. myDBFile = [[DBFile alloc] // allocate and initialize
  1508. init: test.dbf ]; // data file test.dbf
  1509.  
  1510. . /*
  1511. . * here the database file can be used, records can be
  1512. . * read, modified or written back.
  1513. . */
  1514.  
  1515. [myDBFile free]; // close data file and free resources
  1516. }
  1517.  
  1518. Access to a DBFile is record-oriented. Every DBFile object contains a buffer 
  1519. area large enough to store the data of exactly one record. 
  1520.  
  1521. This record buffer can be filled with a record already stored in the database 
  1522. file and can be written back to disk. Additionally the record can be modified 
  1523. by the application program using the database library. 
  1524.  
  1525. The program fragment shown above allocates and initializes a  DBFile object and 
  1526. opens a database file called test.dbf for reading and writing. 
  1527.  
  1528. This program---whose functionality will be extended throughout this and the 
  1529. next chapter---can be found in \usr\samples\dbtest after installing the sample 
  1530. files. 
  1531.  
  1532. It defines a simple database file with two fields. The first field is called 
  1533. NAME. It can hold a string with a maximum length of 30 characters, the second 
  1534. is called PHONE, and is able to store a string with a maximum length of 20 
  1535. characters. 
  1536.  
  1537. The following records are already stored in the database file: 
  1538.  
  1539. ΓöîΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  1540. ΓöéNr. ΓöéNAME      ΓöéPHONE     Γöéremark    Γöé
  1541. Γö£ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  1542. Γöé0   ΓöéJoe       Γöé 23987-3  Γöé          Γöé
  1543. Γö£ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  1544. Γöé1   ΓöéFrank     Γöé 6334589  Γöé          Γöé
  1545. Γö£ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  1546. Γöé2   ΓöéSue       Γöé 523593   Γöé          Γöé
  1547. Γö£ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  1548. Γöé3   ΓöéMichael   Γöé 9845-43  Γöédeleted   Γöé
  1549. Γö£ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  1550. Γöé4   ΓöéKurt      Γöé 2543     Γöé          Γöé
  1551. ΓööΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  1552.  
  1553. As you can see, the database file contains five records. The fourth one is 
  1554. marked as deleted. In the following, it will be shown how access to this file 
  1555. is achieved. 
  1556.  
  1557. DBFile provides a method to read in a record from the associated database file. 
  1558. This method is called -readRecord:. It takes exactly one parameter, which is 
  1559. the position where in the file the record is stored. After reading the record, 
  1560. the data is copied into the record buffer where it can be accessed by the 
  1561. application program. Take care never to use direct record access using 
  1562. -readRecord: (or the accompanying method for modifying a record, -writeRecord:) 
  1563. when access to the records using an index file is enabled. 
  1564.  
  1565. The access to the data is accomplished by using methods of objects of type 
  1566. DBField or one of its subclasses. DBField implements the methods -stringValue 
  1567. to read the data stored in the record buffer and -setStringValue: to write data 
  1568. into the record buffer. 
  1569.  
  1570. When initializing a DBFile object, all needed DBField objects are automatically 
  1571. created and can be used thereafter by the application program. 
  1572.  
  1573. Access to a specfic DBField object is provided through the  -field: method of 
  1574. DBFile. If you want to print the  NAME field of the currently loaded record, 
  1575. you could, for example, type 
  1576.  
  1577.   printf ("Name: %s\n",[[myDBFile field:0] stringValue]) 
  1578.  
  1579. Now, here's a simple program (it is stored as dbtest1.m in \usr\samples\dbtest) 
  1580. which reads all records and prints the name and the phone number of every 
  1581. entry: 
  1582.  
  1583. #include <db/db.h>
  1584.  
  1585. main ()
  1586. {
  1587. DBFile *myDBFile = [[DBFile alloc] init: test.dbf ];
  1588. int i;
  1589.  
  1590. printf ( NAME PHONE \n" );
  1591. printf ( ===================================================\n" );
  1592. for (i = 0;i < 5;i++)
  1593. {
  1594. [myDBFile readRecord: i];
  1595. printf (  ,[[myDBFile field:0] stringValue],
  1596. [[myDBFile field:1] stringValue]);
  1597. }
  1598.  
  1599. [myDBFile free];
  1600. }
  1601.  
  1602. Using -readRecord: all records are read and by using the  -stringValue method 
  1603. of DBField, the two fields NAME and PHONE are printed to stdout. 
  1604.  
  1605. To compile this program simply type in 
  1606.  
  1607.   gcc -o dbtest1.exe dbtest1.m -lobjcdb -lobjcutil -lobjc 
  1608.  
  1609. As you may have noticed the name and the phone number of Michael are also 
  1610. printed after compiling, linking and starting the application. It seems the 
  1611. library does not take note of the fact that some records are marked as deleted 
  1612. and some are not. 
  1613.  
  1614. Using -readRecord: the application program can access all stored records, even 
  1615. those, which have been marked as deleted. There is an instance method of DBFile 
  1616. called -deleted, returning a boolean value, to determine if the current record 
  1617. is deleted or not. 
  1618.  
  1619. So by extending the program with the line 
  1620.  
  1621.   if (![myDBFile deleted]) 
  1622.  
  1623. before the line printing the contents, only those records, which are not marked 
  1624. as deleted would be printed. Look at dbtest2.m to see the whole source code 
  1625. including this simple modification. 
  1626.  
  1627. In this program, the number of records stored in the database is hard-coded 
  1628. into the program. But normally, we do not know, how many records there are 
  1629. stored in a database file. Therefore a method called -recordCount was 
  1630. implemented for instances of the class DBFile. This method returns the number 
  1631. of records stored in the database file. 
  1632.  
  1633. This example just reads every record stored in the database and prints only 
  1634. those records, which are not marked as deleted. As many programs are likely to 
  1635. use this kind of "linear addressing scheme", two methods are implemented which 
  1636. allow to read all active (not deleted) records sequentially. These two methods 
  1637. are called -findFirst and -findNext. 
  1638.  
  1639. -findFirst tries to find the first active record in the database. It starts 
  1640. searching at the beginning of the database file, first checking record 0. This 
  1641. method returns NO, if there is no active record in the whole database. On 
  1642. success YES is returned and the first active record of the database 
  1643. file---eventually determined by index search order---is loaded into the buffer 
  1644. area. 
  1645.  
  1646. -findNext then searches for the next appearance of an active record in the 
  1647. file. If no more active records are in the database, NO is returned. In case a 
  1648. record was found, its data is copied to the record buffer and the method 
  1649. returns YES. 
  1650.  
  1651. Rewriting the application dbtest2.m to dbtest3.m utilizing these methods would 
  1652. result in the following simple program: 
  1653.  
  1654. #include <db/db.h>
  1655.  
  1656. main ()
  1657. {
  1658. DBFile *myDBFile = [[DBFile alloc] init: test.dbf ];
  1659.  
  1660. if ([myDBFile findFirst]) {
  1661. printf ( NAME PHONE \n" );
  1662. printf ( ===================================================\n" );
  1663.  
  1664. do {
  1665. printf (  ,[[myDBFile field:0] stringValue],
  1666. [[myDBFile field:1] stringValue]);
  1667. } while ([myDBFile findNext]);
  1668. }
  1669. [myDBFile free];
  1670. }
  1671.  
  1672. This program first checks, whether an active record exists, and only if this 
  1673. condition is met, the title lines are printed. Thereafter the record itself is 
  1674. printed and the next record is read in. This procedure of printing a record and 
  1675. reading a new record is continued until -findNext notifies the program that no 
  1676. more active records exist. 
  1677.  
  1678. Using the -delete method, you can mark a record as deleted. The changes are 
  1679. written to the database file immediately. The record is only marked as deleted 
  1680. in the database file, it is not removed from disk automatically. This enables 
  1681. you to undelete a record if needed. 
  1682.  
  1683. Deleting the second record, the name and phone number of Frank, would look like 
  1684. this: 
  1685.  
  1686. [myDBFile readRecord: 1]; // read record
  1687. [myDBFile delete]; // mark record as deleted
  1688.  
  1689. Again note that---just alike array elements in C---the indices of the five 
  1690. records range from 0 to 4. 
  1691.  
  1692. As simple as deleting a record, you can mark an already deleted record as 
  1693. active again. Just use -undelete. The following two lines will mark the fourth 
  1694. record, Michaels name and phone number, as active again: 
  1695.  
  1696. [myDBFile readRecord: 3]; // read record
  1697. [myDBFile undelete]; // mark record as active
  1698.  
  1699. When using -findFirst and -findNext the index of the record in the buffer area 
  1700. can be queried using the instance -currentRecord. This can provide useful when 
  1701. editing more than one record in memory at once. Remember that undeleting a 
  1702. record is not possible when using an index file. 
  1703.  
  1704. The order of the records as returned by -findFirst and -findNext depends on the 
  1705. "natural" order the records are stored in the database file. In case a 
  1706. (secondary) index is used, the retrieve order normally reflects the sort order 
  1707. of the index key. For more information see the Reference manual. 
  1708.  
  1709.  
  1710. ΓòÉΓòÉΓòÉ 7. Modifying Data ΓòÉΓòÉΓòÉ
  1711.  
  1712. As shown in the last chapter, opening a database file and reading records from 
  1713. it is quite simple. Some pages ago I mentioned that the database file is opened 
  1714. for reading and writing. This chapter will describe how to modify data and 
  1715. append new records to an existing database file. 
  1716.  
  1717. In this chapter, an application for managing the data in the previously 
  1718. introduced database file test.dbf will be created. This application shall be 
  1719. able to 
  1720.  
  1721.      display all records stored in the database, 
  1722.      add new records, 
  1723.      modify existing records, 
  1724.      delete record and 
  1725.      mark deleted records as active again. 
  1726.  
  1727.  For this purpose a new class called AddressDatabase is defined which can 
  1728.  handle all these functions as instance methods. The interface declaration for 
  1729.  this class looks like this: 
  1730.  
  1731.   @interface AddressDatabase : Object {
  1732.   DBFile *database;
  1733.   }
  1734.  
  1735.   - init;
  1736.   - free;
  1737.  
  1738.   - (int) menu;
  1739.   - printInfo;
  1740.   - deleteRecord;
  1741.   - undeleteRecord;
  1742.   - addRecord;
  1743.   - modifyRecord;
  1744.   @end
  1745.  
  1746.  The class is derived from Object and has only a single instance variable 
  1747.  called database, which ist used to store a pointer to the DBFile instance 
  1748.  responsible for all database operations. 
  1749.  
  1750.  The two methods -init and -free are implemented to create the DBFile instance 
  1751.  and initialize it, respectively to free it. 
  1752.  
  1753.  -menu shall print all active records stored in the database and display a 
  1754.  simple menu, where the user of the application can choose what he wants to do. 
  1755.  
  1756.  -printInfo is used to print a list of all active records in the database file. 
  1757.  
  1758.  -deleteRecord and -undeleteRecord will ask the user, which record he wants to 
  1759.  mark as deleted or active, and then commit the task of deleting or activating 
  1760.  the specified record. 
  1761.  
  1762.  -addRecord prompts the user for the necessary data (NAME, PHONE), which is to 
  1763.  be stored in a new record and then appends this newly created record to the 
  1764.  database file. 
  1765.  
  1766.  -modifyRecord on the other hand allows the user to change the data of a record 
  1767.  already stored in the database file. 
  1768.  
  1769.  The program will look like this: 
  1770.  
  1771.   #include <db/db.h>
  1772.  
  1773.   @interface AddressDatabase : Object {
  1774.   .
  1775.   .
  1776.   .
  1777.   @end
  1778.  
  1779.   @implementation AddressDatabase
  1780.   .
  1781.   .
  1782.   .
  1783.   @end
  1784.  
  1785.   main ()
  1786.   {
  1787.   AddressDatabase *mydb = [[AddressDatabase alloc] init];
  1788.   int chosen;
  1789.  
  1790.   while ((chosen = [mydb menu]) != 5) {
  1791.   switch (chosen) {
  1792.   case 1:
  1793.   [mydb addRecord];
  1794.   break;
  1795.   case 2:
  1796.   [mydb modifyRecord];
  1797.   break;
  1798.   case 3:
  1799.   [mydb deleteRecord];
  1800.   break;
  1801.   case 4:
  1802.   [mydb undeleteRecord];
  1803.   default: ;
  1804.   }
  1805.   }
  1806.  
  1807.   [mydb free];
  1808.   }
  1809.  
  1810.  As you can see, the main function only creates and initializes the object mydb 
  1811.  and then displays and queries the menu. If menu selection 5 is chosen (end 
  1812.  program), mydb is freed again and the application terminates. 
  1813.  
  1814.  
  1815. ΓòÉΓòÉΓòÉ 7.1. "-init" and "-free" ΓòÉΓòÉΓòÉ
  1816.  
  1817. At the beginning, we will implement the two methods -init, which is the 
  1818. proposed constructor method for AddressDatabase and -free, which is the 
  1819. destructor method. 
  1820.  
  1821. - init
  1822. {
  1823. [super init];
  1824. database = [[DBFile alloc] init: test.dbf ];
  1825. return self;
  1826. }
  1827.  
  1828. - free
  1829. {
  1830. [database free];
  1831. return [super free];
  1832. }
  1833.  
  1834. It should be obvious what these simple methods are doing so no further 
  1835. discussion on them is necessary. 
  1836.  
  1837.  
  1838. ΓòÉΓòÉΓòÉ 7.2. Printing all Records in the Database File ΓòÉΓòÉΓòÉ
  1839.  
  1840. -printInfo is used to read all active records and print their contents to 
  1841. stdout (stdout is normally connected to the window or fullscreen session the 
  1842. program is started from). This is accomplished by the following lines: 
  1843.  
  1844. - printInfo
  1845. {
  1846. if ([database findFirst]) {
  1847. printf ( Nr. NAME PHONE \n" );
  1848. printf ( =======================================================\n" );
  1849.  
  1850. do {
  1851. printf (  ,
  1852. [database currentRecord],
  1853. [[database field:0] stringValue],
  1854. [[database field:1] stringValue]);
  1855. } while ([database findNext]);
  1856. }
  1857. }
  1858.  
  1859. This looks like the simple program dbtest3 which was described in the previous 
  1860. chapter. In addition to printing the fields NAME and PHONE, the number of the 
  1861. record is printed in the first column. The number of the currently read record 
  1862. (the record fetched into the internal record buffer of the DBFile object) can 
  1863. be queried using -currentRecord. 
  1864.  
  1865.  
  1866. ΓòÉΓòÉΓòÉ 7.3. Displaying the Menu ΓòÉΓòÉΓòÉ
  1867.  
  1868. To provide an easy to use user-interface---which is by no means as elegant as a 
  1869. PM application program---a simple menu is printed, to let the user choose what 
  1870. actions he wants to perform on the database file. 
  1871.  
  1872. This user-interface is implemented in the method -menu, which displays all 
  1873. active records and a menu, and then lets the user choose which program function 
  1874. he would like to execute. -menu returns the number of the chosen program 
  1875. function; a list of these functions is shown in the following table: 
  1876.  
  1877. ΓöîΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  1878. ΓöéNr. ΓöéProgram function              Γöé
  1879. Γö£ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  1880. Γöé1   Γöéadd new record                Γöé
  1881. Γö£ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  1882. Γöé2   Γöémodify existing record        Γöé
  1883. Γö£ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  1884. Γöé3   Γöédelete record                 Γöé
  1885. Γö£ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  1886. Γöé4   Γöémark record as active         Γöé
  1887. Γö£ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  1888. Γöé5   Γöéexit program                  Γöé
  1889. ΓööΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  1890.  
  1891. This method---just a simple sequence of C statements, without using some of the 
  1892. methods provided by the database library---is implemented like this: 
  1893.  
  1894. - (int) menu
  1895. {
  1896. int chosen;
  1897.  
  1898. printf ( \n Address Database\n \n" );
  1899.  
  1900. [self printInfo];
  1901.  
  1902. printf ( \n (1) ... add Record (2) ... modify Record\n" );
  1903. printf ( (3) ... delete Record (4) ... restore Record\n" );
  1904. printf ( (5) ... quit Program\n" );
  1905. printf ( \n What shall I do? );
  1906. scanf (  ,&chosen);
  1907. printf ( \n" );
  1908.  
  1909. return chosen;
  1910. }
  1911.  
  1912.  
  1913. ΓòÉΓòÉΓòÉ 7.4. Deleting a Record ΓòÉΓòÉΓòÉ
  1914.  
  1915. - deleteRecord
  1916. {
  1917. long recNumber;
  1918.  
  1919. printf ( \nWhich record shall I delete? );
  1920. scanf (  ,&recNumber);
  1921.  
  1922. [database readRecord: recNumber];
  1923.  
  1924. if ([database deleted]) {
  1925. printf ( \nThis record is already deleted!\n" );
  1926. return nil;
  1927. }
  1928.  
  1929. [database delete];
  1930.  
  1931. return self;
  1932. }
  1933.  
  1934.  
  1935. ΓòÉΓòÉΓòÉ 7.5. Marking a Record as Active ΓòÉΓòÉΓòÉ
  1936.  
  1937. - undeleteRecord
  1938. {
  1939. int i;
  1940. int j = [database recordCount];
  1941. BOOL found = FALSE;
  1942.  
  1943. for (i = 0;i < j;i++)
  1944. {
  1945. [database readRecord: i];
  1946. if ([database deleted]) {
  1947. if (!found) {
  1948. printf ( Nr. NAME PHONE \n" );
  1949. printf ( =======================================================\n" );
  1950. found = TRUE;
  1951. }
  1952. printf (  ,i,
  1953. [[database field:0] stringValue],
  1954. [[database field:1] stringValue]);
  1955. }
  1956. }
  1957.  
  1958. if (!found) {
  1959. printf ( No deleted records found!\n \n" );
  1960. return nil;
  1961. }
  1962.  
  1963. printf ( \nWhich record shall I restore? );
  1964. scanf (  ,&i);
  1965.  
  1966. [database readRecord: i];
  1967.  
  1968. if (![database deleted]) {
  1969. printf ( \nThis record is not deleted, no need to restore!\n" );
  1970. return nil;
  1971. }
  1972.  
  1973. [database undelete];
  1974.  
  1975. return self;
  1976. }
  1977.  
  1978.  
  1979. ΓòÉΓòÉΓòÉ 7.6. Adding a New Record ΓòÉΓòÉΓòÉ
  1980.  
  1981. - addRecord
  1982. {
  1983. char nameField[31];
  1984. char phoneField[21];
  1985.  
  1986. printf ( \nName : );
  1987. scanf (  ,nameField);
  1988.  
  1989. printf ( Phone: );
  1990. scanf (  ,phoneField);
  1991.  
  1992. [database clear];
  1993. [[database field: 0] setStringValue: nameField];
  1994. [[database field: 1] setStringValue: phoneField];
  1995. [database append];
  1996.  
  1997. return self;
  1998. }
  1999.  
  2000.  
  2001. ΓòÉΓòÉΓòÉ 7.7. Modifying an Existing Record ΓòÉΓòÉΓòÉ
  2002.  
  2003. - modifyRecord
  2004. {
  2005. long recNumber;
  2006. char nameField[31];
  2007. char phoneField[21];
  2008.  
  2009. printf ( \nWhich record would you like to modify? );
  2010. scanf (  ,&recNumber);
  2011.  
  2012. [database readRecord: recNumber];
  2013.  
  2014. if ([database deleted]) {
  2015. printf ( \nThis record is deleted! You can't modify it!\n" );
  2016. return nil;
  2017. }
  2018.  
  2019. printf ( \nName : );
  2020. scanf (  ,nameField);
  2021.  
  2022. printf ( Phone: );
  2023. scanf (  ,phoneField);
  2024.  
  2025. [database clear];
  2026. [[database field: 0] setStringValue: nameField];
  2027. [[database field: 1] setStringValue: phoneField];
  2028.  
  2029. [database replace];
  2030.  
  2031. return self;
  2032. }
  2033.  
  2034. This program is stored as dbtest4.m in \usr\samples\dbtest. You can compile it 
  2035. by typing 
  2036.  
  2037.  gcc -o dbtest4.exe dbtest4.m -lobjcdb -lobjcutil -lobjc 
  2038.  
  2039. Note that this application does very little error-checking. It is not meant to 
  2040. be an end-user application, but only to demonstrate the usage of the diverse 
  2041. methods provided by the DBFile class. 
  2042.  
  2043. As you can see, it is really simple to utilize the provided class DBFile to 
  2044. create your own database applications. Most of the code shown above is 
  2045. concerned with printing information on the screen and prompt for user actions. 
  2046. In the next chapter you will see, how to combine the features of the OS/2 PM 
  2047. class library and the database library in a simple Presentation Manager 
  2048. application. 
  2049.  
  2050.  
  2051. ΓòÉΓòÉΓòÉ 8. A Sample PM Application Using the Database Library ΓòÉΓòÉΓòÉ
  2052.  
  2053. This chapter will demonstrate the concepts of integrating the classes of the PM 
  2054. library with the one provided by the database library to create an application 
  2055. used to store phone numbers and e-mail addresses. 
  2056.  
  2057.  
  2058. ΓòÉΓòÉΓòÉ 8.1. Purpose of the Application ΓòÉΓòÉΓòÉ
  2059.  
  2060. This sample program will be used to demonstrate an implementation of a 
  2061. presentation manager application DBase III files. In addition to this the 
  2062. program provides a framework for an address database which can eventually 
  2063. provide useful to some developers. The main focus was not laid upon creating a 
  2064. full-featured and easy-to-use application program but to show the concepts of 
  2065. combining the two different topics dealt with up to now in this document. 
  2066.  
  2067. The application will store records in a database having a structure as 
  2068. specified in Table~tab:addformat. 
  2069.  
  2070.  
  2071. ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  2072. ΓöéField.ΓöéName    ΓöéLengΓöéType      ΓöéDescription         Γöé
  2073. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2074. Γöé1     ΓöéNAME    Γöé40  ΓöéCharacter ΓöéName of the person. Γöé
  2075. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2076. Γöé2     ΓöéADDRESS Γöé40  ΓöéCharacter ΓöéAddress of the      Γöé
  2077. Γöé      Γöé        Γöé    Γöé          Γöéperson.             Γöé
  2078. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2079. Γöé3     ΓöéPHONE   Γöé40  ΓöéCharacter ΓöéPhone number of the Γöé
  2080. Γöé      Γöé        Γöé    Γöé          Γöéperson.             Γöé
  2081. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2082. Γöé4     ΓöéFAX     Γöé40  ΓöéCharacter ΓöéFax number of the   Γöé
  2083. Γöé      Γöé        Γöé    Γöé          Γöéperson.             Γöé
  2084. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2085. Γöé5     ΓöéEMAIL   Γöé40  ΓöéCharacter ΓöéE-Mail address of   Γöé
  2086. Γöé      Γöé        Γöé    Γöé          Γöéthe person.         Γöé
  2087. ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  2088.  
  2089. Data format used for storing addresses 
  2090.  
  2091. The main window of the application will contain a single listbox, only 
  2092. displaying the first field of every record (NAME). 
  2093.  
  2094. Adding new records, editing records, deleting records and displaying all 
  2095. information stored for a specific record will be realized with dialog windows. 
  2096.  
  2097.  
  2098.  
  2099. Main window of address database
  2100.  
  2101. Figure fig:addmain shows the main window of the address database displaying 
  2102. some records. 
  2103.  
  2104.  
  2105. ΓòÉΓòÉΓòÉ 8.2. Application Menu ΓòÉΓòÉΓòÉ
  2106.  
  2107. To process user actions the application will provide a menu bar with two menus 
  2108. in it. 
  2109.  
  2110. The menu File will only provide one menu item to let the user exit the 
  2111. application. It is named Exit. 
  2112.  
  2113. The second menu used by this program is called Data. Here all actions 
  2114. concerning data manipulation, such as adding new records, deleting records etc. 
  2115. can be found. 
  2116.  
  2117. The menu items found here are New... to add a new record to the end of the 
  2118. database, Edit... to change the data stored for the selected record in the 
  2119. listbox, Delete to delete a record from the database and Info... to display the 
  2120. information associated with the selected record in the listbox control. 
  2121.  
  2122. This program will only display the records which are not marked as deleted in 
  2123. the database file. Deleting a record only marks it as deleted, but by means of 
  2124. this program, the record cannot be marked as active again. 
  2125.  
  2126.  
  2127. ΓòÉΓòÉΓòÉ 8.3. Database File ΓòÉΓòÉΓòÉ
  2128.  
  2129. The program automatically opens a database file called "address.dbf" in the 
  2130. current directory at startup and writes the changes to this file when exiting 
  2131. the application. 
  2132.  
  2133. An empty database template file is provided in \usr\samples\address; it is 
  2134. stored with the name "empty.dbf". 
  2135.  
  2136.  
  2137. ΓòÉΓòÉΓòÉ 8.4. Classes Used in the Application ΓòÉΓòÉΓòÉ
  2138.  
  2139. In addition to the standard PM and database classes only one new class is 
  2140. required. This class will be called Controller. It will implement all necessary 
  2141. methods required by the program to carry out its job, for example record 
  2142. insertion etc. 
  2143.  
  2144. The methods defined here can be identified as belonging to three different 
  2145. classes. The initializing methods are used to open the database file and read 
  2146. in all records at program start. The database access methods will be called 
  2147. directly from the appropriate menu items and all display dialog windows to 
  2148. change or view the data stored. At last, the delegate methods are implemented, 
  2149. which are used to react to some user actions, such as resizing the window and 
  2150. exiting the program. 
  2151.  
  2152.  
  2153.  
  2154. Dialog window to add a new record
  2155.  
  2156. In addition to these methods, Controller defines many instance variables which 
  2157. are used by some of the methods to access the database or the dialog windows on 
  2158. the screen. 
  2159.  
  2160.  
  2161. ΓòÉΓòÉΓòÉ 8.4.1. Instance Variables ΓòÉΓòÉΓòÉ
  2162.  
  2163. The following instance variables are defined and used by this class. 
  2164.  
  2165. @interface Controller : Object
  2166. {
  2167. StdDialog *insertRecord;
  2168. StdDialog *replaceRecord;
  2169. StdDialog *infoRecord;
  2170.  
  2171. id insertName;
  2172. id insertAddress;
  2173. id insertPhone;
  2174. id insertFax;
  2175. id insertEMail;
  2176. id replaceName;
  2177. id replaceAddress;
  2178. id replacePhone;
  2179. id replaceFax;
  2180. id replaceEMail;
  2181. id infoName;
  2182. id infoAddress;
  2183. id infoPhone;
  2184. id infoFax;
  2185. id infoEMail;
  2186.  
  2187. DBFile *database;
  2188. DBList *recordList;
  2189. }
  2190.  
  2191. -insertRecord, -replaceRecord and -infoRecord are used to store pointers to the 
  2192. dialogs for adding a new record (see Figure~fig:addinsert), editing and saving 
  2193. the data for an existing record (the dialog is shown in Figure~fig:addreplace) 
  2194. and for displaying the information associated with a record (see 
  2195. Figure~fig:addinfo). 
  2196.  
  2197.  
  2198.  
  2199. Dialog window for editing already existing records
  2200.  
  2201. The instance variables defined as ids are initialized to provide easy and fast 
  2202. access to the dialog items themselves. They are initialized at dialog creation. 
  2203.  
  2204. database is a pointer to a DBFile object which is used to retrieve and save 
  2205. records from and to the database file itself. 
  2206.  
  2207. recordList is a list object to enable the application to hold more than one 
  2208. record at a time in memory. This provides faster and easier access to the 
  2209. single records. When the program is started, all record information is 
  2210. retrieved into this object and when the application exits all modified or new 
  2211. records are written to the database again. 
  2212.  
  2213.  
  2214.  
  2215. Dialog window used to display all information stored in a record
  2216.  
  2217.  
  2218. ΓòÉΓòÉΓòÉ 8.4.2. Initializing Methods ΓòÉΓòÉΓòÉ
  2219.  
  2220. The initializing methods are used to initialize the object itself and all 
  2221. objects used by it. Additionally, the destructor method -free is also described 
  2222. here. 
  2223.  
  2224.      -init is used to create all dialog windows in memory. Here all instance 
  2225.       variables of this object are initialized. Additionally, the database file 
  2226.       is opened and the record list object is initialized. 
  2227.      -free is the destructor method of this object. Here all objects are 
  2228.       destroyed again. 
  2229.      -readList: retrieves all records stored in the database file into the 
  2230.       record list and afterwards displays the first data field of every record 
  2231.       in the listbox. See Figure~fig:addmain for an example main window as 
  2232.       created and filled by this method. 
  2233.  
  2234.  The Controller object used in this application is created and initialized by 
  2235.  the main () function of the program. This function also creates the main 
  2236.  window, also including the listbox, and calls -readList:. 
  2237.  
  2238.  The method -init of the Controller class looks like this: 
  2239.  
  2240.   - init
  2241.   {
  2242.   insertRecord = [[StdDialog alloc] initWithId: IDD_INSREP];
  2243.   replaceRecord = [[StdDialog alloc] initWithId: IDD_INSREP];
  2244.   infoRecord = [[StdDialog alloc] initWithId: IDD_INFO];
  2245.  
  2246.   [insertRecord setText: Insert new Record ];
  2247.   [replaceRecord setText: Replace existing Record ];
  2248.  
  2249.   [insertRecord createObjects];
  2250.   [replaceRecord createObjects];
  2251.   [infoRecord createObjects];
  2252.  
  2253.   insertName = [insertRecord findFromID: IDD_NAMEENTRY];
  2254.   insertAddress = [insertRecord findFromID: IDD_ADDRESSENTRY];
  2255.   insertPhone = [insertRecord findFromID: IDD_PHONEENTRY];
  2256.   insertFax = [insertRecord findFromID: IDD_FAXENTRY];
  2257.   insertEMail = [insertRecord findFromID: IDD_EMAILENTRY];
  2258.   replaceName = [replaceRecord findFromID: IDD_NAMEENTRY];
  2259.   replaceAddress = [replaceRecord findFromID: IDD_ADDRESSENTRY];
  2260.   replacePhone = [replaceRecord findFromID: IDD_PHONEENTRY];
  2261.   replaceFax = [replaceRecord findFromID: IDD_FAXENTRY];
  2262.   replaceEMail = [replaceRecord findFromID: IDD_EMAILENTRY];
  2263.   infoName = [infoRecord findFromID: IDD_NAMEENTRY];
  2264.   infoAddress = [infoRecord findFromID: IDD_ADDRESSENTRY];
  2265.   infoPhone = [infoRecord findFromID: IDD_PHONEENTRY];
  2266.   infoFax = [infoRecord findFromID: IDD_FAXENTRY];
  2267.   infoEMail = [infoRecord findFromID: IDD_EMAILENTRY];
  2268.  
  2269.   database = [[DBFile alloc] init: address.dbf ];
  2270.   recordList = [[DBList alloc] initForDatabase: database];
  2271.  
  2272.   return self;
  2273.   }
  2274.  
  2275.  In the beginning the three necessary dialog windows are created. Note, that 
  2276.  the dialog windows for adding a new record and editing an existing record are 
  2277.  created from the same dialog template. They only differ in the dialog title. 
  2278.  Therefore the next two lines set the title strings for these two dialog 
  2279.  windows to either "Insert new Record" or "Replace existing record". 
  2280.  
  2281.  Afterwards the user interface objects in the dialogs are created and the 
  2282.  instance variables used to simplify access to them are initialized. 
  2283.  
  2284.  In the end the database object is created and initialized and the database 
  2285.  file "address.dbf" is opened. The record list used to hold all records in 
  2286.  memory is created and the method returns to the caller. 
  2287.  
  2288.  -free only frees all objects associated or created with/by this object. The 
  2289.  simple source code for this method looks like this: 
  2290.  
  2291.   - free
  2292.   {
  2293.   [[recordList freeObjects] free];
  2294.  
  2295.   [database free];
  2296.   [insertRecord free];
  2297.   [replaceRecord free];
  2298.   [infoRecord free];
  2299.  
  2300.   return [super free];
  2301.   }
  2302.  
  2303.  The last initializer methods---called -readList:---is used to fill the record 
  2304.  list with all data in the database. This can simply be accomplished by calling 
  2305.  [recordList fetchAllRecords: nil]. The only parameter -fetchAllRecords: takes 
  2306.  is totally ignored by the DBList-object. It is only used for the method to 
  2307.  become an action method which can provide useful when using the visual 
  2308.  development tools. 
  2309.  
  2310.  Then the first data field of every record is extracted from the list and 
  2311.  displayed in the listbox in the main window. 
  2312.  
  2313.   - readList: sender;
  2314.   {
  2315.   ListBox *nameListBox = [sender findFromID: IDD_PUSHBUTTON1];
  2316.   int i;
  2317.  
  2318.   [recordList fetchAllRecords];
  2319.  
  2320.   for (i = 0;i < [recordList count];i++) {
  2321.   [nameListBox insertItem: LIT_END
  2322.   text: [[[recordList objectAt: i] field: 0] stringValue]];
  2323.   }
  2324.  
  2325.   return self;
  2326.   }
  2327.  
  2328.  You can see that a simple for-loop is used to visit all records in the list. 
  2329.  Then the string stored in the first data field is appended to the listbox as a 
  2330.  new listbox item. The class DBRecordList is a subclass of SimpleList which can 
  2331.  be found in the utility library (objcutil.a). For detailed information on the 
  2332.  methods for access to single objects stored in the list, see the reference 
  2333.  manual. Just as DBFile every DBRecord object contains a list of fields which 
  2334.  can be used for access to the field data. 
  2335.  
  2336.  
  2337. ΓòÉΓòÉΓòÉ 8.4.3. Database Access Methods ΓòÉΓòÉΓòÉ
  2338.  
  2339. To modify the information stored in the database file and displayed in the 
  2340. listbox four methods---called -insert:, -replace:, -info: and -delete:---have 
  2341. been implemented. These methods are called directly as response to user 
  2342. actions. Therefore these methods have to be declared as actions, resulting in 
  2343. every method having exactly one parameter called sender. 
  2344.  
  2345. The simplest of these methods is -delete:. This method shall display a message 
  2346. box querying the user if he really wants to delete the selected record. If no 
  2347. record was selected -delete: has to return nil without further processing. 
  2348.  
  2349. - delete: sender
  2350. {
  2351. ListBox *nameListBox = [sender findFromID: IDD_PUSHBUTTON1];
  2352. SHORT selected = [nameListBox selected];
  2353.  
  2354. if (selected < 0)
  2355. return nil;
  2356.  
  2357. .
  2358. .
  2359. .
  2360.  
  2361. return self;
  2362. }
  2363.  
  2364. This piece of source code first queries a pointer to the listbox object using 
  2365. -findFromID: of the sending object, which always is the main window. This 
  2366. pointer is stored in nameListBox. The variable selected is used to store the 
  2367. index of the selected item in the listbox. If no item is selected, selected is 
  2368. less than 0. 
  2369.  
  2370. Additionally, the following variables are declared in this method: 
  2371.  
  2372. char numberBuffer[6];
  2373. long numberOfRecord;
  2374. char *nameBuffer;
  2375.  
  2376. The working part of the method first shows a message box querying the user if 
  2377. he really wants to delete the record, and then marks the record as deleted and 
  2378. removes it from the listbox. The record must also be deleted from the record 
  2379. list. 
  2380.  
  2381. if (WinMessageBox (HWND_DESKTOP,[sender window],
  2382. Do you really want to delete the selected Item? ,
  2383. Addresses ,
  2384. 0,MB_YESNO | MB_QUERY) == MBID_YES) {
  2385. numberOfRecord = atoi(numberBuffer);
  2386.  
  2387. [database readRecord: [[recordList objectAt: selected] recNo]];
  2388.  
  2389. [nameListBox deleteItem: selected];
  2390. [database delete];
  2391. [recordList deleteRecordAt: selected];
  2392. }
  2393.  
  2394. For a description of WinMessageBox() see the OS/2 PM API documentation. 
  2395.  
  2396. The next method, -info:, is used to display a dialog window which was created 
  2397. throughout the initialization sequence of the Controller object (infoRecord) 
  2398. and fill the entry fields with the data associated with the selected record. 
  2399.  
  2400. The lines of source code for the interesting part of the implementation are 
  2401. shown below: 
  2402.  
  2403. - info: sender
  2404. {
  2405. .
  2406. .
  2407. .
  2408.  
  2409. record = [recordList objectAt: selected];
  2410.  
  2411. [infoName setText: [[record field: 0] string]];
  2412. [infoAddress setText: [[record field: 1] string]];
  2413. [infoPhone setText: [[record field: 2] string]];
  2414. [infoFax setText: [[record field: 3] string]];
  2415. [infoEMail setText: [[record field: 4] string]];
  2416.  
  2417. [infoRecord runModalFor: sender];
  2418.  
  2419. return self;
  2420. }
  2421.  
  2422. A pointer to the record object representing the selected listbox item is stored 
  2423. in record.Afterwards the text in the data fields is displayed in the entry 
  2424. field objects, and the dialog window is run modal for the main window. 
  2425.  
  2426. The instance method -replace: is used to edit the data of an already existing 
  2427. record. In the beginning the corresponding entry fields must be filled with the 
  2428. strings stored in the data fields of the selected record, as shown previously. 
  2429.  
  2430. After processing the dialog the data must be copied from the entry fields into 
  2431. the record list and the database file is updated to reflect the changes. 
  2432.  
  2433. - replace: sender
  2434. {
  2435. .
  2436. .
  2437. .
  2438. [replaceRecord runModalFor: sender];
  2439.  
  2440. if ([replaceRecord result] == DID_OK) {
  2441. nameBuffer = [replaceName text: NULL];
  2442. addressBuffer = [replaceAddress text: NULL];
  2443. phoneBuffer = [replacePhone text: NULL];
  2444. faxBuffer = [replaceFax text: NULL];
  2445. emailBuffer = [replaceEMail text: NULL];
  2446.  
  2447. [[record field: 0] setStringValue: nameBuffer];
  2448. [[record field: 1] setStringValue: addressBuffer];
  2449. [[record field: 2] setStringValue: phoneBuffer];
  2450. [[record field: 3] setStringValue: faxBuffer];
  2451. [[record field: 4] setStringValue: emailBuffer];
  2452.  
  2453. [record replace];
  2454.  
  2455. [nameListBox deleteItem: selected];
  2456. [nameListBox insertItem: selected text: nameBuffer];
  2457.  
  2458. free (nameBuffer);
  2459. free (addressBuffer);
  2460. free (phoneBuffer);
  2461. free (faxBuffer);
  2462. free (emailBuffer);
  2463. }
  2464.  
  2465. return self;
  2466. }
  2467.  
  2468. As the memory used for temporarily storing the strings in the entry field 
  2469. controls is allocated automatically by -text: this buffer area must be freed 
  2470. again later using free (). The implementation is really simple. The text 
  2471. strings are retrieved from the entry fields, written into the DBRecord object 
  2472. and then the modifications are written back to the database file. 
  2473.  
  2474. The implementation of -insert: is just the same as replace: with the 
  2475. difference, that the entry fields must be initialized with empty strings 
  2476. because a new record is allocated at the end. In contrast to -replace: this 
  2477. method will store the field data directly in the database buffer area of the 
  2478. DBFile object and will not use the buffer area of an already existing DBRecord 
  2479. object in the record list. After appending the record to the database 
  2480. file---using the DBFile method -append---a new DBRecord object is created and 
  2481. inserted into the record list. It has to be taken care of also updating the 
  2482. display area of the listbox control. 
  2483.  
  2484. These functionality can be achieved by implementing insert: as shown below. For 
  2485. better understanding the complete code for this method is shown: 
  2486.  
  2487. - insert: sender
  2488. {
  2489. ListBox *nameListBox = [sender findFromID: IDD_PUSHBUTTON1];
  2490. char *nameBuffer;
  2491. char *addressBuffer;
  2492. char *phoneBuffer;
  2493. char *faxBuffer;
  2494. char *emailBuffer;
  2495.  
  2496. [database clear];
  2497.  
  2498. [insertName setText: ];
  2499. [insertAddress setText: ];
  2500. [insertPhone setText: ];
  2501. [insertFax setText: ];
  2502. [insertEMail setText: ];
  2503.  
  2504. [insertRecord runModalFor: sender];
  2505.  
  2506. if ([insertRecord result] == DID_OK) {
  2507. nameBuffer = [insertName text: NULL];
  2508. addressBuffer = [insertAddress text: NULL];
  2509. phoneBuffer = [insertPhone text: NULL];
  2510. faxBuffer = [insertFax text: NULL];
  2511. emailBuffer = [insertEMail text: NULL];
  2512.  
  2513. [[database field: 0] setStringValue: nameBuffer];
  2514. [[database field: 1] setStringValue: addressBuffer];
  2515. [[database field: 2] setStringValue: phoneBuffer];
  2516. [[database field: 3] setStringValue: faxBuffer];
  2517. [[database field: 4] setStringValue: emailBuffer];
  2518.  
  2519. [database append];
  2520. [recordList addObject: [[DBRecord alloc] initForEntity: database]];
  2521.  
  2522. [nameListBox insertItem: LIT_END text: nameBuffer];
  2523.  
  2524. free (nameBuffer);
  2525. free (addressBuffer);
  2526. free (phoneBuffer);
  2527. free (faxBuffer);
  2528. free (emailBuffer);
  2529. }
  2530.  
  2531. return self;
  2532. }
  2533.  
  2534. This method appends a new record to the data file and also creates a new data 
  2535. object at the end of the record list. At last a new item is inserted and 
  2536. displayed in the listbox control. 
  2537.  
  2538. All methods described above are fully responsible for displaying all necessary 
  2539. information (dialog windows, message boxes) and leave the database and the 
  2540. record list in memory in a consistent state. Do not forget to update all 
  2541. storage structures (record list, data file, listbox) every time you modify, add 
  2542. or delete one of the records. 
  2543.  
  2544. The next two methods are used as delegate methods for the main window. 
  2545.  
  2546.  
  2547. ΓòÉΓòÉΓòÉ 8.4.4. Delegate Methods ΓòÉΓòÉΓòÉ
  2548.  
  2549. As shown in Section~sec:resizing, the following simple method is called every 
  2550. time the size of the main window is changed by the user. Its only purpose is to 
  2551. adjust the size of the listbox to the size of the main window. 
  2552.  
  2553. - windowDidResize: sender
  2554. {
  2555. ListBox *nameListBox = [sender findFromID: IDD_PUSHBUTTON1];
  2556.  
  2557. [nameListBox setSize: 0:0:[sender width]:[sender height]];
  2558. return self;
  2559. }
  2560.  
  2561. Instead of implementing -windowDidResize: the window object used in this 
  2562. application could be used directly to react to a user action of resizing the 
  2563. window. If only a single window object is displayed in the client area of an 
  2564. instance of StdWindow or MainWindow the window object is capable to take care 
  2565. of resizing its client window itself. To set the client window of an object use 
  2566. -setClientWindow:. This will be the preferred way of handling this special 
  2567. situation when using the Interface Editor program. An example of this can be 
  2568. found in Chapter~cha:ibintro. 
  2569.  
  2570. For a more complete description of the methods used here and the other ones 
  2571. provided by the database library, see the reference manual. 
  2572.  
  2573.  
  2574. ΓòÉΓòÉΓòÉ 8.5. The main() Function of the Application ΓòÉΓòÉΓòÉ
  2575.  
  2576. The main () function of the application can be found in the file addresses.m. 
  2577. It only performs the standard actions, as creating and initializing the 
  2578. objects, and after creating all necessary objects but before starting execution 
  2579. of the application, the record list is read in by calling [controller readList: 
  2580. mainWindow]. The complete source code is shown below. 
  2581.  
  2582. Here you can see that all menu items which are used are bound directly to the 
  2583. previously described methods of the class Controller. Only Exit will directly 
  2584. call -performClose: of the main window. 
  2585.  
  2586. main()
  2587. {
  2588. StdApp *addresses = [[StdApp alloc] init];
  2589. MainWindow *mainWindow = [[MainWindow alloc] initWithId: IDD_MAIN
  2590. andFlags: (FCF_MENU | FCF_ACCELTABLE |
  2591. FCF_SIZEBORDER)];
  2592. Controller *controller = [[Controller alloc] init];
  2593.  
  2594. [mainWindow setTitle: Addresses ];
  2595.  
  2596. [mainWindow setDelegate: controller];
  2597. [mainWindow bindCommand: IDM_EXIT withObject: mainWindow
  2598. selector: @selector(performClose:)];
  2599. [mainWindow bindCommand: IDM_NEWAD withObject: controller
  2600. selector: @selector(insert:)];
  2601. [mainWindow bindCommand: IDM_EDITAD withObject: controller
  2602. selector: @selector(replace:)];
  2603. [mainWindow bindCommand: IDM_INFOAD withObject: controller
  2604. selector: @selector(info:)];
  2605. [mainWindow bindCommand: IDM_DELETEAD withObject: controller
  2606. selector: @selector(delete:)];
  2607.  
  2608. [mainWindow createObjects];
  2609.  
  2610. [mainWindow insertChild: [[ListBox alloc] initWithId: IDD_PUSHBUTTON1
  2611. andFlags: WS_VISIBLE | WS_TABSTOP
  2612. in: mainWindow]];
  2613.  
  2614. [controller readList: mainWindow];
  2615.  
  2616. [mainWindow makeKeyAndOrderFront: nil];
  2617.  
  2618. [addresses run];
  2619.  
  2620. [mainWindow free];
  2621. [addresses free];
  2622. }
  2623.  
  2624.  
  2625. ΓòÉΓòÉΓòÉ 9. Using the Container Class ΓòÉΓòÉΓòÉ
  2626.  
  2627. One of the most interesting window classes provided by the OS/2 Presentation 
  2628. Manager is the container class. Newly introduced with OS/2 2.0, it provides a 
  2629. storage for general objects of any type. These objects can be displayed in 
  2630. several different styles: as icons, as text, as a combination of both, 
  2631. or---very similar to multi-column listboxes---in the so-called detail's view, 
  2632. also supporting direct editing of the values shown. 
  2633.  
  2634. This chapter will show how to use the Objective C class  Container, which is 
  2635. provided by objcpm.a. Section~sec:container.simple will show how to create a 
  2636. container from scratch and display the contents as icons. The following 
  2637. sections will present a simple example of using the container as a multi-column 
  2638. listbox including direct editing. 
  2639.  
  2640.  
  2641. ΓòÉΓòÉΓòÉ 9.1. Simple Container---Icon Display ΓòÉΓòÉΓòÉ
  2642.  
  2643. Containers can be used to store any data you want. The only restriction set up 
  2644. is that all data which is to be stored in a container object must be 
  2645. encapsulated in an Objective C object. 
  2646.  
  2647. The information presented in this section assumes that you already have a 
  2648. simple PM application including a main window. The container which will be used 
  2649. here will fill the whole client area of the main window. 
  2650.  
  2651.  
  2652. ΓòÉΓòÉΓòÉ 9.1.1. Creating the Application ΓòÉΓòÉΓòÉ
  2653.  
  2654. Here you will once more be shown how the typical source code for creating a PM 
  2655. application looks like---including creation and display of a main window. 
  2656.  
  2657. #include <pm/pm.h>
  2658.  
  2659. main ()
  2660. {
  2661. StdApp *application = [[StdApp alloc] init];
  2662. MainWindow *mainwindow = [[MainWindow alloc]
  2663. initWithId: 1000
  2664. andFlags: FCF_SIZEBORDER];
  2665.  
  2666. [mainwindow createObjects];
  2667.  
  2668. [mainwindow makeKeyAndOrderFront: nil];
  2669.  
  2670. // => here goes the container stuff
  2671.  
  2672. [application run];
  2673.  
  2674. [mainwindow free];
  2675. [application free];
  2676. }
  2677.  
  2678.  
  2679. ΓòÉΓòÉΓòÉ 9.1.2. Step One: Creating the Container ΓòÉΓòÉΓòÉ
  2680.  
  2681. The first thing we will have to do is to declare a new variable of type 
  2682. (Container *), which we will use to access the container control. 
  2683.  
  2684. So, add Container *container to the declaration section of the main function. 
  2685.  
  2686. Creating the object itself is just as simple as creating any other window 
  2687. object. Using -initWithId: andFlags: in: a new object, previously allocated 
  2688. with +alloc, will be initialized. 
  2689.  
  2690. container = [[Container alloc] initWithId: 1001
  2691. andFlags: (CCS_MINIRECORDCORE |
  2692. WS_VISIBLE)
  2693. in: mainwindow];
  2694. [mainwindow insertChild: container];
  2695. [container setSize: 0:0:[mainwindow width]:[mainwindow height]];
  2696.  
  2697. The container object is created using the flags  CCS_MINIRECORDCORE and 
  2698. WS_VISIBLE. The second one only tells the container to be visible. 
  2699. CCS_MINIRECORDCORE is used to specify which kind of data will be stored in the 
  2700. container. At the moment only this style is supported. Do not use the flag 
  2701. CCS_RECORDCORE. 
  2702.  
  2703. Then the container is defined to be a child window of  mainwindow, and the size 
  2704. of the newly created control is set accordingly to fill the complete client 
  2705. region of its parent window. 
  2706.  
  2707.  
  2708. ΓòÉΓòÉΓòÉ 9.1.3. Step Two: Inserting Data ΓòÉΓòÉΓòÉ
  2709.  
  2710. After creation it is possible to insert data into the container. As was stated 
  2711. before all kinds of Objective C objects can be stored in a container object. 
  2712. For simplicity, only instances of Object are used here. 
  2713.  
  2714. Insertion of single objects itself can be performed using three distinct 
  2715. methods, -insertObject:, -insertObject: withTitle: and -insertObject: 
  2716. withTitle: andIcon:. If you are going to display the data stored in the 
  2717. container as icons you will normally use the third method shown above because 
  2718. it lets you decide on which icon will be used for display. To insert many 
  2719. objects at once you can use the instance methods -insertObjectList:, 
  2720. -insertObjectList: withTitles: and -insertObjectList: withTitles: andIcon:. 
  2721. These methods are prepared to process many single objects stored in instances 
  2722. of SimpleList. See the reference manual for more information. There you can 
  2723. also find information concerning a hierarchical structure of the objects in a 
  2724. container object as is used in the tree view. 
  2725.  
  2726.      -insertObject: ... inserts a new object into the container. In Icon View, 
  2727.       no Icon text will be displayed. The Icon used for this object will be the 
  2728.       clock-mouse pointer. 
  2729.      -insertObject: withTitle: ... analogous to  insertObject: an object is 
  2730.       inserted into the container, but here a title text can be specified. 
  2731.      -insertObject: withTitle: andIcon: ... here all data, which will be 
  2732.       displayed in Icon View, can be specified. The Icon parameter must be a 
  2733.       valid Icon resource. To query the resource handle of a valid Icon 
  2734.       resource e.g. use the functions WinQuerySysPointer() or WinLoadPointer(). 
  2735.      The methods -insertObjectList:, -insertObjectList: withTitles: and 
  2736.       -insertObjectList: withTitles: andIcon: are used to insert a list of 
  2737.       objects into a container object at once. The first parameter---which is 
  2738.       the only one for -insertObjectList:---is a SimpleList instance containing 
  2739.       the objects to be inserted. The second parameter is another list object 
  2740.       containing objects storing the object titles. Every object must implement 
  2741.       a -stringValue method. E.g. use istances of String. The third and last 
  2742.       parameter is the resource handle of a single valid icon resource for all 
  2743.       objects. 
  2744.  
  2745.  We will use the first three methods described above to insert three different 
  2746.  objects into the container. The sample code for the record-insertion is: 
  2747.  
  2748.   [container insertObject: [[Object alloc] init]]; // first object
  2749.  
  2750.   [container insertObject: [[Object alloc] init]
  2751.   withTitle: Title of object ]; // second object
  2752.  
  2753.   [container insertObject: [[Object alloc] init]
  2754.   withTitle: Another object
  2755.   andIcon: WinQuerySysPointer (HWND_DESKTOP,
  2756.   SPTR_APPICON,
  2757.   FALSE)]; // third object
  2758.   [container arrange];
  2759.  
  2760.  The icon resource used for the third object is queried using the API function 
  2761.  WinQuerySysPointer(). Here the resource handle for the current application 
  2762.  Icon is queried. 
  2763.  
  2764.  
  2765.  
  2766.   First container sample "cont1.exe"
  2767.  
  2768.  At the end an -arrange message is sent to the container to re-arrange the 
  2769.  icons now displayed. This is necessary in this example program, otherwise all 
  2770.  icons would cover the same space in the container in the lower left corner. 
  2771.  
  2772.  
  2773. ΓòÉΓòÉΓòÉ 9.1.4. Complete Source Code ΓòÉΓòÉΓòÉ
  2774.  
  2775. After inserting this code into the application program, the source code should 
  2776. look like this. 
  2777.  
  2778. #include <pm/pm.h>
  2779.  
  2780. main ()
  2781. {
  2782. StdApp *application = [[StdApp alloc] init];
  2783. MainWindow *mainwindow = [[MainWindow alloc]
  2784. initWithId: 1000
  2785. andFlags: FCF_SIZEBORDER];
  2786. Container *container;
  2787.  
  2788. [mainwindow createObjects];
  2789.  
  2790. [mainwindow makeKeyAndOrderFront: nil];
  2791.  
  2792. container = [[Container alloc] initWithId: 1001
  2793. andFlags: (CCS_MINIRECORDCORE |
  2794. WS_VISIBLE)
  2795. in: mainwindow];
  2796. [mainwindow insertChild: container];
  2797. [container setSize: 0:0:[mainwindow width]:[mainwindow height]];
  2798.  
  2799. [container insertObject: [[Object alloc] init]]; // first object
  2800.  
  2801. [container insertObject: [[Object alloc] init]
  2802. withTitle: Title of object ]; // second object
  2803.  
  2804. [container insertObject: [[Object alloc] init]
  2805. withTitle: Another object
  2806. andIcon: WinQuerySysPointer (HWND_DESKTOP,
  2807. SPTR_APPICON,
  2808. FALSE)]; // third object
  2809. [container arrange];
  2810.  
  2811. [application run];
  2812.  
  2813. [mainwindow free];
  2814. [application free];
  2815. }
  2816.  
  2817. Just compile and link this program: 
  2818.  
  2819.   gcc -o cont1.exe cont1.m -lobjcpm -lobjc
  2820.   emxbind -ep cont1.exe
  2821.  
  2822. Figure~fig:container.simple shows the output produced by this application. 
  2823.  
  2824.  
  2825. ΓòÉΓòÉΓòÉ 9.2. Using the Details View ΓòÉΓòÉΓòÉ
  2826.  
  2827. The details view of a container object looks like a multi-column Listbox. In 
  2828. addition to the features of a simple Listbox control the container class makes 
  2829. it possible to edit the data displayed by simply selecting it---using the 
  2830. system-defined direct-manipulation action for editing an objects title text, 
  2831. which normally is depressing the Alt-key and single-clicking with mouse button 
  2832. 1 on the text---and entering the new data. 
  2833.  
  2834. Container objects in details view are widely used across the user interface 
  2835. provided by the Workplace Shell. Every folder object can be opened in details 
  2836. view to provide access to all data associated with the data or program objects 
  2837. stored inside. 
  2838.  
  2839. As already mentioned before a container object in details view looks like a 
  2840. multi-column Listbox. So---in contrast to all other views of container 
  2841. objects---the data is always sorted in some way. In the details view, some kind 
  2842. of ordering---in a mathematical sense---is used to determine the position of 
  2843. the records in the display area. Every record (item) stored in a container is 
  2844. displayed in a single line of the container. Every data field of the items is 
  2845. displayed in one column. 
  2846.  
  2847.  
  2848. ΓòÉΓòÉΓòÉ 9.2.1. Creating Columns ΓòÉΓòÉΓòÉ
  2849.  
  2850. To utilitze the details view we first have to specify the number of columns to 
  2851. be displayed in the container object. After allocation and initialization using 
  2852. +alloc and -initWithId: andFlags: in: the program must create all columns 
  2853. needed for display. This is done using -addColumn:. 
  2854.  
  2855. -addColumn: takes one parameter of type (char *), which is the column title. 
  2856. This column title normally is displayed at the head of the column in the 
  2857. topmost line of the display area of the container object. 
  2858.  
  2859. Adding columns at the moment is done one column at a time. So creating more 
  2860. than one column results in calling -addColumn: for each of them. Take care that 
  2861. the created column is always appended to the list of columns already existing. 
  2862. There is no way of deleting columns or rearranging them. 
  2863.  
  2864. After creating the columns the container window should be displayed in details 
  2865. view, which can be accomplished using -detailView:. Below is a simple piece of 
  2866. source code adding four columns to a container object and switching the display 
  2867. to details view. 
  2868.  
  2869. .
  2870. .
  2871. [container addColumn: first Column ];
  2872. [container addColumn: second Column ];
  2873. [container addColumn: third Column ];
  2874. [container addColumn: fourth Column ];
  2875.  
  2876. [container detailView: self];
  2877. .
  2878. .
  2879.  
  2880. You should add all used columns in the beginning. If you do not do so you must 
  2881. manually change the data stored for all records already stored in the container 
  2882. object. As you normally will know which columns will be needed for a container 
  2883. object at creation time of the object, this complicated procedure is not 
  2884. described here. 
  2885.  
  2886.  
  2887. ΓòÉΓòÉΓòÉ 9.2.2. Which Data is Displayed? ΓòÉΓòÉΓòÉ
  2888.  
  2889. Normally OS/2 container windows store all kinds of data structures as items. 
  2890. When using this library container windows can store all kinds of Objective C 
  2891. objects. But how does the container window know what data shall be displayed in 
  2892. every column? 
  2893.  
  2894. Plain OS/2 API programming makes this quite difficult. You have to define a 
  2895. data structure, put pointers to some memory areas in it, where strings to be 
  2896. displayed are really stored, and every column must be initialized with an index 
  2897. pointer to the column data. 
  2898.  
  2899. The container class only expects the objects stored as items to implement some 
  2900. methods to provide access to the data. 
  2901.  
  2902. Every object must implement the usual initializer and destructor methods -init 
  2903. and -free. As -init is never called directly, you can also use some other 
  2904. method. -free is used whenever an item is deleted, and it must take care of 
  2905. freeing all memory allocated by the object. 
  2906.  
  2907. To provide access to the single data fields, the methods -setFieldData: 
  2908. withString: and -fieldData: must be present. If only read-only access, i.e., 
  2909. data is only to be displayed, is required, only the -fieldData: method hast to 
  2910. be implemented. 
  2911.  
  2912. -setFieldData: (ULONG) field withString: (char *) aString is used to set a 
  2913. string to field number field. Field numbering starts at 0. Do not expect 
  2914. aString to be a pointer to a (const char) area. This memory area can be reused 
  2915. again, so your method must copy the data to a memory area in your object. 
  2916.  
  2917. -(char *) fieldData: (ULONG) field is used to retrieve a pointer to the string 
  2918. stored for field field. As the data pointed to is never modified by the 
  2919. container object, this method can return a pointer to the memory area used 
  2920. internally for storing the data. 
  2921.  
  2922. A simple class designed to store four fields could look like this: 
  2923.  
  2924. @interface ContainerObject : Object
  2925. {
  2926. char fields[4][30];
  2927. }
  2928.  
  2929. - init;
  2930.  
  2931. - setFieldData: (ULONG) field withString: (char *) aString;
  2932. - (char *) fieldData: (ULONG) field;
  2933.  
  2934. @end
  2935.  
  2936. As no data will be allocated dynamically, the class does not have to implement 
  2937. a -free method. 
  2938.  
  2939. The implementation is just as simple as 
  2940.  
  2941. @implementation ContainerObject
  2942. - init
  2943. {
  2944. [super init];
  2945.  
  2946. fields[0][0] = 0x0;
  2947. fields[1][0] = 0x0;
  2948. fields[2][0] = 0x0;
  2949. fields[3][0] = 0x0;
  2950.  
  2951. return self;
  2952. }
  2953.  
  2954. - setFieldData: (ULONG) field withString: (char *) aString
  2955. {
  2956. if (field > 3) // field number not valid ?
  2957. return nil;
  2958.  
  2959. strncpy (fields[field],aString,29); // copy at most 29 chars
  2960. fields[field][29] = 0x0; // last character is NULL
  2961.  
  2962. return self;
  2963. }
  2964.  
  2965. - (char *) fieldData: (ULONG) field
  2966. {
  2967. if (field > 3) // field number not valid ?
  2968. return NULL;
  2969. else
  2970. return fields[field];
  2971. }
  2972.  
  2973. @end
  2974.  
  2975. Note that the method -init is only implemented to initialize the four strings, 
  2976. so the data displayed is always valid. 
  2977.  
  2978. -setFieldData: withString: just copies at most 29 characters into the buffer 
  2979. area for the string. As strncpy() does not append a NULL character if the 
  2980. source string is longer than the specified maximum size, the 30^th character is 
  2981. set to 0x0. 
  2982.  
  2983. -fieldData: just returns a pointer to the internal buffer area of the field 
  2984. data referenced by field. 
  2985.  
  2986. -free must be implemented if any dynamic allocation is used by the object. 
  2987. Otherwise the default method inherited from the superclass will provide enough 
  2988. functionality. 
  2989.  
  2990.  
  2991. ΓòÉΓòÉΓòÉ 9.2.3. Direct Editing ΓòÉΓòÉΓòÉ
  2992.  
  2993. Direct editing support is not implemented at this time. If you want to use 
  2994. this, you have to catch the window message CN_REALLOCPSZ and return the 
  2995. appropriate value. 
  2996.  
  2997. When using static storage, it should be enough to just catch the message and 
  2998. return TRUE. Take care, that also the title string of the column can be edited, 
  2999. so a distinction must be made. 
  3000.  
  3001. A sample implementation of the -handleMessage: withParams: and:: method of a 
  3002. delegate object of the window containing the container object could look like 
  3003. this: 
  3004.  
  3005. - (MRESULT) handleMessage: (ULONG) msg withParams: (MPARAM) mp1
  3006. and: (MPARAM) mp2 : sender
  3007. {
  3008. // catch WM_CONTROL message of type CN_REALLOCPSZ
  3009. if ((msg == WM_CONTROL) && (SHORT2FROMMP (mp1) == CN_REALLOCPSZ)) {
  3010. CNREDITDATA *cnrEditData = PVOIDFROMMP (mp2);
  3011.  
  3012. // check what text was edited; either column data or title data
  3013. switch (cnrEditData->id) {
  3014. case CID_LEFTDVWND: // column data was edited
  3015. return (MRESULT) TRUE; // just return TRUE, memory is static!
  3016.  
  3017. case CID_LEFTCOLTITLEWND: { // column title was edited
  3018. char **buffer = (char **) cnrEditData->ppszText; // old text
  3019. char *buffer2 = (char *) malloc (cnrEditData->cbText + 1); // new text
  3020.  
  3021. free (*buffer); // free memory occupied by the column title
  3022. *buffer = buffer2; // set memory area for new column title. The
  3023. // data is copied into this area automatically
  3024. return (MRESULT) TRUE; // memory for column title was allocated
  3025.  
  3026. default: // nothing for us!
  3027. return (MRESULT) FALSE;
  3028. }
  3029. }
  3030.  
  3031. This looks quite complicated. But simply this means that the container window 
  3032. sends a message asking the program if the new text (length is given in 
  3033. cnrEditData->cbText) should be copied or not. If TRUE is returned, the text is 
  3034. copied to the string pointed to by *(cnrEditData->ppszText). When this message 
  3035. is sent, *(cnrEditData->ppszText) stores a pointer to the string currently 
  3036. stored. 
  3037.  
  3038. If you do not want the column title or the data to be copied, just return 
  3039. FALSE. 
  3040.  
  3041. Formatting the columns or setting read-only flags can be done separately for 
  3042. the column data and the column title. Just use -setColumnTitleAttributes: to 
  3043. set the attributes for the title data and -setColumnDataAttributes: to set the 
  3044. data attributes. See the description of the Container class in the reference 
  3045. manual for more information. 
  3046.  
  3047.  
  3048. ΓòÉΓòÉΓòÉ 10. Creating an Application using the Visual Development Tools ΓòÉΓòÉΓòÉ
  3049.  
  3050. Up to now this document was mainly concerned with some kind of "raw" 
  3051. programming using the library package provided by this product. If you 
  3052. carefully read through these sections you should have got some knowledge of the 
  3053. structure of an OS/2 PM program written in Objective C and already got a 
  3054. limited overview of some of the classes which can be directly used by end-user 
  3055. applications. 
  3056.  
  3057. Throughout the rest of this document the use of the Visual Development Tools 
  3058. will be discussed. These tools were created to let the users benefit from the 
  3059. dynamic structure of the Objective C language on the one hand, and, on the 
  3060. other hand, to free application developers from the time-consuming tasks of 
  3061. e.g. writing makefiles and doing most of the standard initialization work. 
  3062.  
  3063. In the beginning an overview of the development tools themselves will be 
  3064. presented. The use of each of them will be discussed shortly and some of the 
  3065. limitations---most of them will be removed in later versions of the 
  3066. package---are pointed out. 
  3067.  
  3068.  
  3069. ΓòÉΓòÉΓòÉ 10.1. Visual Development Tools ΓòÉΓòÉΓòÉ
  3070.  
  3071. The development tools shipped together with the library package at the moment 
  3072. consist of three programs. The Database Editor, the Interface Editor and the 
  3073. Project Editor. Additionally, a program called Console is provided which is 
  3074. quite useful when debugging your own applications. 
  3075.  
  3076.  
  3077. ΓòÉΓòÉΓòÉ 10.1.1. The Database Editor Application ΓòÉΓòÉΓòÉ
  3078.  
  3079. The Database Editor Application is used to create empty database template 
  3080. files. You must use this program to set up a database structure before 
  3081. accessing a DBase file. Instead of this program e.g. DBase itself could be used 
  3082. to set up the database structure. 
  3083.  
  3084. At the moment, you cannot modify existing database files. This limitation will 
  3085. be dealt with in one of the next versions, then providing full access to every 
  3086. DBase file. 
  3087.  
  3088.  
  3089. ΓòÉΓòÉΓòÉ 10.1.2. The Project Editor Application ΓòÉΓòÉΓòÉ
  3090.  
  3091. This program is the core of the development environment. It provides program 
  3092. functions for interactively creating classes, and it is designed to handle all 
  3093. necessary tasks to maintain the files in a project. 
  3094.  
  3095. You can tell the program what files your application is built from, and it can 
  3096. then generate a makefile suitable for compilation. Just alike folders on the 
  3097. Workplace Shell, the files are organized in some categories and can be added, 
  3098. removed or edited. 
  3099.  
  3100. When creating classes some skeleton implementation and header files are 
  3101. generated for you. The only thing you have to do is to fill in these "class 
  3102. templates". Additionally, a main function suited for most applications is 
  3103. created automatically. 
  3104.  
  3105. The program also provides some kind of control center where you can edit source 
  3106. code files from or compile and test your application. 
  3107.  
  3108. At the moment, interaction with the other development tools (e.g. the Interface 
  3109. Editor application) is not fully implemented. In future versions it will be 
  3110. possible to start editing for all files of a project from this central place. 
  3111. The visual development tools will be more closely integrated in future versions 
  3112. of this development system. 
  3113.  
  3114.  
  3115. ΓòÉΓòÉΓòÉ 10.1.3. The Interface Editor Application ΓòÉΓòÉΓòÉ
  3116.  
  3117. While the Project Editor application is used to edit project data, this program 
  3118. is used to interactively create a program's user interface. In contrast to 
  3119. normal dialog editor programs you can also create instances of some classes of 
  3120. your application and access other objects from the library package. 
  3121.  
  3122. Most window classes of the libraries are implemented up to now. In the 
  3123. following versions, easy access to the database classes will be provided, too. 
  3124.  
  3125. In addition to creating user interface elements---and even Objective C 
  3126. objects---interactively, this program also allows you to set up connections 
  3127. between these objects. Most of the commands can be bound to actions 
  3128. interactively and the outlet connections of the objects can be initialized by 
  3129. using the drag and drop features of this application. 
  3130.  
  3131. The data is saved in an Objective C typed stream, just storing the objects you 
  3132. create interactively on disk, and can be loaded easily by your application 
  3133. program. 
  3134.  
  3135. Using this program can save you lots of time; you just create the user 
  3136. interface and program objects visually and when your application starts, 
  3137. everything gets initialized automatically. 
  3138.  
  3139. This and the following chapters will provide an introduction into this kind of 
  3140. magic; just try it, and you will see how simple development can be. 
  3141.  
  3142.  
  3143. ΓòÉΓòÉΓòÉ 10.2. "Textview"---The Easy Way ΓòÉΓòÉΓòÉ
  3144.  
  3145. For demonstration issues an application already discussed in this manual will 
  3146. be developed using the visual development tools. The purpose of one of the 
  3147. applications dealt with previously was to simply read a text file and display 
  3148. its contents in a window. Throughout the rest of this chapter the "Textview" 
  3149. application will be developed newly. 
  3150.  
  3151.  
  3152.  
  3153.  
  3154. Project Template
  3155.  
  3156. First, drag a project template (see Figure~fig:project-template) from the 
  3157. Development Folder to a folder somewhere located on your drives. You should not 
  3158. use the Desktop folder or a folder whose name does contain special characters 
  3159. such as spaces as your new project directory because the make program normally 
  3160. will have troubles accessing directories of this kind. Using the OS/2 direct 
  3161. manipulation facilities, you should rename the project folder from its original 
  3162. name "Objc_project" to some name of your choice, e.g. "Textview.prj". 
  3163. Figure~fig:new-project shows an icon view of the "samples" folder, where this 
  3164. project can already be found. 
  3165.  
  3166.  
  3167.  
  3168.  
  3169. "samples" folder containing the new Project
  3170.  
  3171. Opening the new project folder will reveal its contents as depicted in 
  3172. Figure~fig:project-contents. There you can see the files "pb.prj", "pb.cls", 
  3173. "main.oib", "Makefile", and "Main.m". 
  3174.  
  3175.  
  3176.  
  3177.  
  3178. Contents of the project folder when created newly fromthe project template
  3179.  
  3180. The project data itself is stored in the two files "pb.prj" and "pb.cls". 
  3181. Normally an association of files ending in ".prj" should have been set up while 
  3182. installing the development package, so starting the Project Editor application 
  3183. can be achieved by simply double-clicking on "pb.prj". After either newly 
  3184. starting Project Editor by opening the data file or by manually loading the 
  3185. project data file "pb.prj" from the menu bar of the Project Editor application 
  3186. you will see the Log page of the program. In this page messages are eventually 
  3187. displayed while compiling or testing the program to be written. 
  3188.  
  3189.  
  3190. ΓòÉΓòÉΓòÉ 10.2.1. Setting up a New Project ΓòÉΓòÉΓòÉ
  3191.  
  3192. Before starting the work you must tell the Project Editor application some 
  3193. information about your project, e.g. the name of the executable file which is 
  3194. to be created. This can be achieved by changing the data on the Linker page of 
  3195. the notebook control representing the project data. 
  3196.  
  3197.  
  3198.  
  3199.  
  3200. Linker page of the project. Here you can set linker flagsand generate a "Makefile" or the source code filecontaining the main() function.
  3201.  
  3202. In Figure~fig:Linker-Settings this page is depicted. The entry field on the top 
  3203. of the page is used to tell the application the name of the executable file. 
  3204. Because in our case the application will be called "textview.exe" just enter 
  3205. this name here. 
  3206.  
  3207. Below this entry field you can choose which of the (standard) libraries you 
  3208. wish to use with this program. If you want to create simple PM programs, just 
  3209. check the box asking you about the PM Library, to also make use of the classes 
  3210. contained in objcdb, also check the field next to DBase Library. The utility 
  3211. classes and the Objective C runtime library are always linked to your 
  3212. application because either of the libraries requires them to be part of the 
  3213. executable program. In the text field you can enter additional linker options 
  3214. or libraries manually. For example to add objects to the list of the library 
  3215. files enter "-lobjects" here; you can also specify any other linker options 
  3216. here. For more information consult the C compiler manuals themselves. 
  3217.  
  3218. For a complete description of every of the controls on this notebook page and 
  3219. on the other pages see the Application Programming Tools Manual. 
  3220.  
  3221. For this time, only modify the page you can see to look like the one shown in 
  3222. Figure~fig:Linker-Settings. 
  3223.  
  3224. After changing this settings, or after adding or removing source code files 
  3225. from the project, you have to generate a new "Makefile". This can be done by 
  3226. just clicking on the pushbutton Makefile also to be found on this notebook 
  3227. page. Before doing so, the current project data has to be saved on disk using 
  3228. File/Save. Do not rely on the Project Editor application to use the current 
  3229. data in memory when generating the "Makefile". This also applies to compiling 
  3230. and/or testing the application after making some changes. Always save the 
  3231. project data to disk and generate a new "Makefile" before compiling/testing the 
  3232. program. 
  3233.  
  3234.  
  3235. ΓòÉΓòÉΓòÉ 10.2.2. Adding a Resource Script ΓòÉΓòÉΓòÉ
  3236.  
  3237. After setting up the project, copy the resource script "Textview.rc" which was 
  3238. already created when discussing the first version of the Textview program to 
  3239. the project directory. Then switch to the third object page of the project 
  3240. notebook. 
  3241.  
  3242.  
  3243.  
  3244.  
  3245. Adding a resource script to the project.
  3246.  
  3247. To add the resource file to the project, simply drag the resource file from the 
  3248. open project folder where a file object labelled "Textview.rc" should be 
  3249. displayed to the objects page reserved for other objects, e.g. resource scripts 
  3250. or simple C source code files. See Figure~fig:Add-Resource where this procedure 
  3251. is depicted. 
  3252.  
  3253.  
  3254.  
  3255.  
  3256. Objects page reserved for resource scripts and C sourcecode files after adding a resource script.
  3257.  
  3258. After adding the resource script file to the project, the corresponding 
  3259. notebook page will reflect the changes immediately. There you can see---as is 
  3260. shown in Figure~fig:Resource-Page---that a new file object has been added to 
  3261. the data area of the page (a simple container control was used for this 
  3262. purpose). The icon indicates that this file is a resource script file. 
  3263.  
  3264. Thus far a new source code file has been added to the project. After saving the 
  3265. project data file and re-generating a new "Makefile" all project data will be 
  3266. updated. Compiling after this stage would create a binary resource file called 
  3267. "Textview.res" from the resource script file and cause this file to be linked 
  3268. with the object files---only "Main.o" at the moment---when starting 
  3269. compilation. 
  3270.  
  3271. In the next section creating a new Objective C class will be discussed as well 
  3272. as creating a skeleton source code (implementation and header file) for this 
  3273. class. 
  3274.  
  3275.  
  3276. ΓòÉΓòÉΓòÉ 10.2.3. Creating a Class Template and Generating Source Code ΓòÉΓòÉΓòÉ
  3277.  
  3278. In addition to the "standard features" any project managemant tool should 
  3279. offer---such as managing the files a project consists of---the Project Editor 
  3280. offers help in writing the source code files of your program itself. The 
  3281. classes used by your application must be defined in some central place to be 
  3282. known by the other development tools, i.e, the Interface Editor. 
  3283.  
  3284. At the moment you can only define the constructs supported by the Interface 
  3285. Editor program, enabling you to graphically create outlet variables and action 
  3286. methods. After doing so the classes can be used by the graphical development 
  3287. tools, e.g. you can create instances of these classes and connect them to other 
  3288. objects. Additionally the Project Editor application relieves you from the 
  3289. annoying work of creating Objective C interface files and implementation files. 
  3290. A skeleton source code is generated for you automatically and you only have to 
  3291. fill in the method templates. Of course, the newly created class is 
  3292. automatically added to the list of classes managed internally and handled 
  3293. correctly when creating a new "Makefile". 
  3294.  
  3295.  
  3296. ΓòÉΓòÉΓòÉ 10.2.4. How the Constructs are Translated to Source Code ΓòÉΓòÉΓòÉ
  3297.  
  3298. When creating a class you can specify a list of outlets and actions to be used 
  3299. as the instance variables respectively instance methods of the class. This 
  3300. section will discuss how these structures are translated into source code when 
  3301. generating the interface and implementation files. 
  3302.  
  3303. Inserting an outlet called anOutlet will result in a declaration line in the 
  3304. variables section of the interface of the class: 
  3305.  
  3306. id anOutlet;
  3307.  
  3308. As actions are simple instance methods having one parameter of type (id), the 
  3309. translation of an action -anAction: yields 
  3310.  
  3311. -anAction: sender;
  3312.  
  3313. in the header file (interface file) and in an implementation template of 
  3314.  
  3315. -anAction: sender
  3316. {
  3317. return self;
  3318. }
  3319.  
  3320. in the implementation file of the class. The only work you will have to do now 
  3321. is to load the implementation file into some text editor and fill in the method 
  3322. template. 
  3323.  
  3324.  
  3325. ΓòÉΓòÉΓòÉ 10.2.5. Creating the Class Template ΓòÉΓòÉΓòÉ
  3326.  
  3327. In the case of our sample application, only one class which will be called 
  3328. Controller is required. The class should be derived directly from Object. Two 
  3329. connections to other objects, to the main window of the application and to the 
  3330. multi-line entry field are needed, resulting in two outlets we will call 
  3331. mainWindow and mleWindow. In addition to that, a single action method to be 
  3332. called from the menu item Open..., must be defined. This method will be called 
  3333. -loadFile:. 
  3334.  
  3335. To create the new class select the menu item Edit classes from the File menu of 
  3336. the project editor. This will cause a dialog window as depicted in 
  3337. Figure~fig:class-editor to be displayed. 
  3338.  
  3339.  
  3340.  
  3341.  
  3342. Dialog for editing the properties of newly createclasses
  3343.  
  3344. First, select the entry field labeled with Name and enter the name of the new 
  3345. class. In our case, just enter Controller into this field. Next, you will enter 
  3346. the name of the superclass into the corresponding field, Superclass. Because 
  3347. our class will be derived directly from Object, it is not necessary to enter 
  3348. the name of the superclass. 
  3349.  
  3350. After the names of the class and of its superclass have been specified, click 
  3351. on the leftmost button at the bottom of the dialog window shown in 
  3352. Figure~fig:class-editor which is labelled with New Class. This will create a 
  3353. new class template in memory. Now you can edit the list of Actions and the list 
  3354. of Outlets. Normally you should not bother about the third list labelled with 
  3355. Commands. This special list is explained in detail in the Application 
  3356. Programming Tools Manual. 
  3357.  
  3358. To insert the single action method, enter the name of the method (including the 
  3359. colon at the end of the method name) in the entry field on top of the 
  3360. corresponding list box. So, just enter -loadFile: and depress Add below the 
  3361. list box. 
  3362.  
  3363. The two outlets are inserted alike, by just specifying every outlet's name in 
  3364. the entry field on top of the Outlets list box followed by clicking on the Add 
  3365. button in the middle section of the dialog window. 
  3366.  
  3367. After entering the two outlet variables called mainWindow and mleWindow all 
  3368. information for the newly created class has been set. Normally, you will now 
  3369. create an interface file---containing the class declaration---and a skeleton 
  3370. implementation file. This is achieved by clicking on Unparse. 
  3371.  
  3372.  
  3373.  
  3374.  
  3375. The Project Editor application will ask you if the newly createdclass should be inserted into the project.
  3376.  
  3377. When creating these files for the first time, you will be asked by the Project 
  3378. Editor program if you want to insert the new class into the project. Answering 
  3379. this question (see Figure~fig:insert-class) by clicking on Yes will cause the 
  3380. interface file "Controller.h" to be inserted into the Headers page of the Files 
  3381. section of the project notebook. Additionally, the class implementation file 
  3382. "Controller.m" is added to the list of classes (page Classes of section Files). 
  3383. When creating a new "Makefile", the implementation file is added to the list of 
  3384. files to be compiled and linked to the application program. 
  3385.  
  3386. Unparse will always create an implementation file only containing method 
  3387. templates. When implementation and/or header files do already exist, you will 
  3388. be warned of this fact by the Project Editor application, but if you decide to 
  3389. overwrite these files, all changes you have made to them previously will be 
  3390. lost. 
  3391.  
  3392.  
  3393.  
  3394.  
  3395. Page Classes of the Files section of theproject notebook after creating and inserting a new class calledController
  3396.  
  3397. After creating the new class and unparsing the interface declaration and the 
  3398. implementation file you can close the class editor and return to the project 
  3399. notebook. Figure~fig:class-page shows how the Classes page of the Files section 
  3400. of the project notebook will look after inserting the newly created class 
  3401. Controller---including the interface file "Controller.h" and the implementation 
  3402. "Controller.m". 
  3403.  
  3404. Before discussing the design of the user interface of the application and using 
  3405. the newly defined class Controller in the project, the automatically generated 
  3406. interface file ("Controller.h", Section~sec:h-file) and the class 
  3407. implementation file ("Controller.m", see Section~sec:m-file) are presented. 
  3408.  
  3409.  
  3410. ΓòÉΓòÉΓòÉ 10.2.6. Interface File of the Class "Controller" ΓòÉΓòÉΓòÉ
  3411.  
  3412. The header file starts with a comment block. After some necessary include 
  3413. statements (the root header files for the three libraries are automatically 
  3414. included) the interface declaration for the class follows. 
  3415.  
  3416. /* -------------------------------------------------------------------
  3417.  
  3418. Project:
  3419.  
  3420. Objective-C interface file for the class Controller
  3421.  
  3422. This file was automatically generated by Project Editor
  3423. COPYRIGHT (C), 1995, Thomas Baier
  3424.  
  3425. Unregistered version
  3426.  
  3427. COPYRIGHT (C), 1996
  3428. ALL RIGHTS RESERVED.
  3429.  
  3430. Date: Rev:
  3431. Sat Jan 06 14:33:21 1996
  3432.  
  3433. */
  3434.  
  3435. #ifndef _CONTROLLER_H_
  3436. #define _CONTROLLER_H_
  3437.  
  3438. /*====================================================================
  3439. Interface of class Controller
  3440. ====================================================================*/
  3441. #include <pm/pm.h>
  3442. #include <db/db.h>
  3443. #include <util/util.h>
  3444.  
  3445. @interface Controller : Object
  3446. {
  3447. id mainWindow;
  3448. id mleWindow;
  3449. }
  3450.  
  3451. /* -------------------------- Initialize -------------------------- */
  3452.  
  3453. /* ----------------------------- Free ----------------------------- */
  3454.  
  3455. /* ----------- Methods for access to Instance Variables ----------- */
  3456.  
  3457. /* ------------------------ Public methods ------------------------ */
  3458.  
  3459. -loadFile: sender;
  3460.  
  3461. /* ----------------------- Private methods ------------------------ */
  3462.  
  3463. /* ---------------------- Archiving methods ----------------------- */
  3464.  
  3465. @end
  3466.  
  3467. #endif
  3468.  
  3469. In the beginning, the instance variables---only the outlets at the moment---are 
  3470. declared, then the declaration of the action methods follows. 
  3471.  
  3472. The methods are divided into six categories, 
  3473.  
  3474.    1. Initialize methods for initialization of an object. Normally the names of 
  3475.       the methods placed here will have the prefix -init. 
  3476.    2. Free methods for deleting an instance of the class and freeing all 
  3477.       resources occupied by the object. 
  3478.    3. Methods for access to Instance Variables variable access methods. As you 
  3479.       should never access instance variables directly from outside the 
  3480.       implementation of the object's class, you must provide methods to read 
  3481.       the information stored and to update the information if this is desired. 
  3482.    4. Public Methods these are methods normally called from other objects; 
  3483.       methods of this kind are sometimes called the services of the class (or 
  3484.       object). Actions defined with the Project Editor application will be 
  3485.       placed here. 
  3486.    5. Private Methods here you should put methods only called by other methods 
  3487.       of the object itself. In addition to that, the declarations of delegate 
  3488.       methods can be put here. Everything not really "public", i.e., not 
  3489.       referenced directly from source code you write for your project, should 
  3490.       go here. 
  3491.    6. Archiving methods the only methods you should put in this section are 
  3492.       those designed for writing the object to a stream (-write:) or restoring 
  3493.       an object from a stream (-read:). The only other methods fitting here are 
  3494.       the ones also concerned with archiving, e.g. -awake or 
  3495.       -awakeFromInterfaceFile. 
  3496.  
  3497.  The implementation file is organized alike. The skeleton implementation file 
  3498.  produced by the Project Editor application is discussed in the next section. 
  3499.  
  3500.  
  3501. ΓòÉΓòÉΓòÉ 10.2.7. Implementation Skeleton for "Controller" ΓòÉΓòÉΓòÉ
  3502.  
  3503. Just as the interface file, the class implementation starts with a comment 
  3504. block. Thereafter the interface file---"Controller.h" in our case---is 
  3505. included. 
  3506.  
  3507. The declaration of the instance variables is repeated then. Take care to modify 
  3508. this declaration in both the interface file and the implementation file when 
  3509. adding, deleting or renaming instance variables. These actions will also 
  3510. normally require the class information stored in the project file to be 
  3511. updated, but only if action methods or outlet instance variables are concerned. 
  3512.  
  3513. /* -------------------------------------------------------------------
  3514.  
  3515. Project:
  3516.  
  3517. Objective-C source file for the class Controller
  3518.  
  3519. This file was automatically generated by Project Editor
  3520. COPYRIGHT (C), 1995, Thomas Baier
  3521.  
  3522. Unregistered version
  3523.  
  3524. COPYRIGHT (C), 1996
  3525. ALL RIGHTS RESERVED.
  3526.  
  3527. Date: Rev:
  3528. Sat Jan 06 14:33:21 1996
  3529.  
  3530. ------------------------------------------------------------------- */
  3531.  
  3532. /* ------------------------ Include files ------------------------- */
  3533.  
  3534. /* ------------------------ Classes used ------------------------ */
  3535.  
  3536. /* ------------------------- Externals -------------------------- */
  3537.  
  3538. /* -------------------------- Defines --------------------------- */
  3539.  
  3540. /* ---------------------- Class variables ----------------------- */
  3541.  
  3542. /*====================================================================
  3543. Implementation of class Controller
  3544. ====================================================================*/
  3545. #include Controller.h
  3546.  
  3547. @implementation Controller : Object
  3548. {
  3549. id mainWindow;
  3550. id mleWindow;
  3551. }
  3552.  
  3553. /*====================================================================
  3554. Initialize
  3555. ====================================================================*/
  3556.  
  3557. /*====================================================================
  3558. Free
  3559. ====================================================================*/
  3560.  
  3561. /*====================================================================
  3562. Methods for access to Instance Variables
  3563. ====================================================================*/
  3564.  
  3565. /*====================================================================
  3566. Public methods
  3567. ====================================================================*/
  3568.  
  3569. /*--------------------------------------------------------------------
  3570. |-loadFile: sender|
  3571.  
  3572. Return self.
  3573. --------------------------------------------------------------------*/
  3574. -loadFile: sender
  3575. {
  3576. return self;
  3577. }
  3578.  
  3579. /*====================================================================
  3580. Private methods
  3581. ====================================================================*/
  3582.  
  3583. /*====================================================================
  3584. Archiving methods
  3585. ====================================================================*/
  3586.  
  3587. @end
  3588.  
  3589. Every action method is preceded by a comment block suitable for putting in 
  3590. required information, as for example a short description of the purpose of the 
  3591. method and the return value. 
  3592.  
  3593. /*--------------------------------------------------------------------
  3594. |-loadFile: sender|
  3595.  
  3596. Return self.
  3597. --------------------------------------------------------------------*/
  3598. -loadFile: sender
  3599. {
  3600. return self;
  3601. }
  3602.  
  3603. By default, all action methods will return a pointer to the instance of the 
  3604. class itself (self). This convention should be followed, if the method exits 
  3605. without error. On error, nil should be returned. 
  3606.  
  3607. New methods should be inserted in the appropriate places in the implementation 
  3608. file. Never forget to update the header file after adding, removing or changing 
  3609. the name of a method or instance variable. In case of adding a new action or 
  3610. outlet, you will also have to change the information stored in the project 
  3611. data. 
  3612.  
  3613. After the class was created, save the project data and recreate the project's 
  3614. "Makefile". 
  3615.  
  3616.  
  3617. ΓòÉΓòÉΓòÉ 10.3. Creating the User Interface ΓòÉΓòÉΓòÉ
  3618.  
  3619. For the moment you can put aside the Project Editor and relax a bit. Up to now 
  3620. a new project was created from scratch---from a project template in other 
  3621. words---a resource script was added and a new class was defined. The project 
  3622. now consists some files, 
  3623.  
  3624.    1. "PB.prj" and "PB.cls" storing the project data and the custom classes 
  3625.       defined by the user. 
  3626.    2. The "Makefile" containing rules on how to compile and link the source 
  3627.       code files together to form an executable program and the source code 
  3628.       files themselves, 
  3629.    3. "Textview.rc", a resource definition file storing information about the 
  3630.       application menu requested by the program, 
  3631.    4. "Main.m", the source code file containing the main() function. This file 
  3632.       should normally not be edited by the application programmer. 
  3633.    5. "Controller.h" and "Controller.m" are the Objective C interface file 
  3634.       respectively the class implementation file for the newly created class 
  3635.       Controller. 
  3636.    6. In addition to these files, there is also a file called "main.oib". This 
  3637.       file is the primary user interface definition. This section will deal 
  3638.       with editing this file and putting the core of the program into it. 
  3639.  
  3640.  Figure~fig:oib-file-icon shows the icon of the user interface file. It can be 
  3641.  found in the project folder you created at the beginning. 
  3642.  
  3643.  
  3644.  
  3645.  
  3646.   Main user interface definition file"main.oib".
  3647.  
  3648.  To start editing the user interface definition, just start the Interface 
  3649.  Editor application by double-clicking on the icon of "main.oib". 
  3650.  
  3651.  After some seconds, the Interface Editor will be loaded and the windows will 
  3652.  be displayed, revealing a user interface similar to the one depicted in 
  3653.  Figure~fig:ib-all. 
  3654.  
  3655.  In the following the term interface file will not be used to refer to file 
  3656.  containing an Objective C class interface declaration (a header file); instead 
  3657.  of this interface file will be a shortcut for user interface file, the kind of 
  3658.  file you will create by using the Interface Editor application. For every 
  3659.  interface file the Interface Editor program will display a separate 
  3660.  window---also containing a menu bar---which will be called the main window of 
  3661.  the interface file. 
  3662.  
  3663.  
  3664.  
  3665.   User interface of the Interface Editor application.
  3666.  
  3667.  When starting the Interface Editor for the first time, only the main window 
  3668.  will be displayed. It is recommended to make the other windows visible by 
  3669.  choosing the appropriate menu items from the View menu. For simple and fast 
  3670.  access to the most-used functions, at least the preferences window and the 
  3671.  tools palette should be displayed automatically. 
  3672.  
  3673.  In contrast to most other OS/2 applications, the desktop itself will be your 
  3674.  work area. Although you might not be accustomed to directly editing your 
  3675.  windows on the desktop, this approach has some advantages over putting 
  3676.  everything inside the display area of the main window of an application. 
  3677.  
  3678.  As you will remember, the user interface of the application to be created will 
  3679.  consist of a simple (main) window having a menu bar and a multi-line entry 
  3680.  field covering the whole display area of this window object. Additionally, a 
  3681.  single instance of the Controller class will be used to respond to user 
  3682.  actions. 
  3683.  
  3684.  To create the necessary objects and establish some connections between them, 
  3685.  the Interface Editor application will be used. The rest of this section will 
  3686.  discuss the look and the use of the windows depicted in Figure~fig:ib-all 
  3687.  while doing the work. 
  3688.  
  3689.  
  3690. ΓòÉΓòÉΓòÉ 10.3.1. Creating Objects---Using the "Class List" ΓòÉΓòÉΓòÉ
  3691.  
  3692. Our journey through the windows of Interface Editor will start with a window 
  3693. labelled with Class List. This simple dialog window consists of a single 
  3694. listbox control and a push button. Figure~fig:class-list shows how the window 
  3695. should look at the moment. 
  3696.  
  3697.  
  3698.  
  3699.  
  3700. Class List window displaying the only custom classknown by the application at the moment.
  3701.  
  3702. In the listbox control all custom classes currently known by the Interface 
  3703. Editor application are shown. When loading an interface file, the program will 
  3704. try to load a class definition file ("PB.cls") from the directory the interface 
  3705. file is loaded from. 
  3706.  
  3707. Using the push button Instantiate, you can create instances of the currently 
  3708. selected class in your interface file. Because one instance of the class 
  3709. Controller is needed in our application, just click once on the button. As a 
  3710. consequence of this, you can see that a new icon is displayed in the main 
  3711. window of the Interface Editor application (see Figure~fig:ib-main-window on 
  3712. Page~fig:ib-main-window). 
  3713.  
  3714. The class definition file is read in automatically when loading an interface 
  3715. file. If you change the class definition by, e.g., adding a new class or 
  3716. changing classes, you will have to initiate the process of updating manually by 
  3717. choosing the menu item File/Reload Classes. 
  3718.  
  3719.  
  3720. ΓòÉΓòÉΓòÉ 10.3.2. Creating User Interface Objects---The "Tools"Palette ΓòÉΓòÉΓòÉ
  3721.  
  3722. The last section was mainly concerned with instantiating user-defined classes. 
  3723. It was mentioned that every object created from a user-defined class is 
  3724. represented by an object in the main window of the Interface Editor program. 
  3725. Every object displayed there consists of an object symbol and an object title. 
  3726.  
  3727. While the icon of the object indicates the type of object, e.g. an instance of 
  3728. a user-defined class or a dialog window, the title of the object is a unique 
  3729. identifier for the object itself. These objects are stored in an instance of 
  3730. the class InterfaceFile which is provided by the PM library. The objects 
  3731. themselves---or to be more exact, pointers to the associated Objective C 
  3732. objects---can be queried by the application program at run-time using the 
  3733. appropriate instance methods of InterfaceFile (e.g. -objectWithTitle:) and the 
  3734. title of the object. 
  3735.  
  3736.  
  3737.  
  3738.  
  3739. Object Types currently supported by Interface Editor
  3740.  
  3741. Figure~fig:ib-object-icons shows the icons of the object types currently 
  3742. supported by the development environment. Starting with the object displayed at 
  3743. the left margin, you can see a user-defined object labelled with Controller, 
  3744. three different user interface objects and two objects representing instances 
  3745. of some other often-used classes in an application. Instances of user-defined 
  3746. classes can be created using the Classes dialog window discussed in the 
  3747. previous section. 
  3748.  
  3749. The user interface objects are a dialog window (an instance of StdDialog) 
  3750. having the title Dialog, a window object---which is an instance of the class 
  3751. StdWindow---labelled with Window and an instance of MainWindow having the same 
  3752. name as the class it is an instance of (main window object). These objects can 
  3753. be created by clicking on one of the push buttons Dialog, Window or Main in the 
  3754. Windows section of the Tools Palette (see Figure~fig:tools-palette). This 
  3755. section will be concerned with creating these windows and other user interface 
  3756. control elements. 
  3757.  
  3758. The other two objects are created by choosing the appropriate menu items from 
  3759. the Create menu located in the main window of an interface file as displayed in 
  3760. Interface Editor. The second object from the right is an instance of the Help 
  3761. class, the rightmost object represents a Printer object. 
  3762.  
  3763. In future versions creating instances of many other non-user interface classes, 
  3764. as for example from classes provided by database library, will be supported. 
  3765.  
  3766. Other user interface objects than dialog, standard and main windows, like push 
  3767. buttons or entry fields, are always part of one of the window objects shown in 
  3768. Figure~fig:ib-object-icons and therefore not displayed separately in the main 
  3769. window of the interface file. 
  3770.  
  3771.  
  3772.  
  3773.  
  3774. The Tools Palette of the Interface Editor application. From here youcan choose the user interface objects you want to add to aninterface file.
  3775.  
  3776. Just alike any program for creating and editing dialogs Interface Editor 
  3777. assists the programmer in designing the user interface of his programs in a 
  3778. very simple way. One of the main advantages over a "normal" dialog editor, 
  3779. creating instances of user defined classes, was already discussed. The more 
  3780. advanced features as, e.g., setting up connections between objects will be 
  3781. dealt with in the next section. Here only the basic creation of the user 
  3782. interface of a program---in our case for the "Textview" application---will be 
  3783. shown. 
  3784.  
  3785.  
  3786. ΓòÉΓòÉΓòÉ 10.3.3. Creating a Window Object ΓòÉΓòÉΓòÉ
  3787.  
  3788. As you will remember, the user interface of our application will only consist 
  3789. of a simple MainWindow containing a multi-line entry field and a menu bar 
  3790. displaying a pull down menu as defined in the resource script "Textview.rc". To 
  3791. create an instance of MainWindow you must use the Tools Palette. If this window 
  3792. (see Figure~fig:tools-palette) is not already displayed on your desktop, choose 
  3793. Tools from the View menu. 
  3794.  
  3795.  
  3796.  
  3797.  
  3798. Main window of the Interface Editor application. Two objects havebeen inserted, an instance of a class called Controllerand a window object labelled with Main Window.
  3799.  
  3800. Now, simply click on the push button labelled with Main in the topmost row of 
  3801. buttons in the dialog window. Immediately, an empty window object will be 
  3802. displayed on the desktop and a new object is created in the main window of the 
  3803. interface file. This window should now look like the window shown in 
  3804. Figure~fig:ib-main-window. 
  3805.  
  3806. Before speaking about customization of this main window the multi-line entry 
  3807. field will be created. 
  3808.  
  3809.  
  3810. ΓòÉΓòÉΓòÉ 10.3.4. Inserting a Multi-Line Entry Field into the Window ΓòÉΓòÉΓòÉ
  3811.  
  3812. To insert the multi-line entry field into the main window just click on the MLE 
  3813. button in the Tools Palette. Any user interface object---except dialog windows, 
  3814. standard windows and main windows---are inserted into the currently selected 
  3815. window object. To determine which object is currently selected, look at the 
  3816. icons representing the objects in the main window of the interface file. You 
  3817. will notice that exactly one object is selected, making it the target window of 
  3818. every insertion of user interface elements (if it is a window object). 
  3819.  
  3820. You can either change the current selection by clicking with mouse button 
  3821. one---which normally is the left mouse button---on the icon representing the 
  3822. the object or you can alternatively select the window object itself if it is 
  3823. currently displayed on the desktop. 
  3824.  
  3825. For more information on selection techniques and manipulating the ordinary 
  3826. object properties see the Application Programming Tools Manual. 
  3827.  
  3828.  
  3829. ΓòÉΓòÉΓòÉ 10.3.5. Editing the Object Properties ΓòÉΓòÉΓòÉ
  3830.  
  3831. Up to now a main window has been created with a multi-line entry field in its 
  3832. display area. In the previous chapters creating a window object required to 
  3833. specify some information at initialization, e.g. the PM identifier and some 
  3834. flags determining the look and feel of the object. As the main window and the 
  3835. multi-line entry field were not created from source code, there must be some 
  3836. other way, some graphical way to set the object properties. 
  3837.  
  3838. To edit the properties of the main window just select it and then have a look 
  3839. at the Preferences window normally displayed in the bottom right corner of the 
  3840. desktop (choose View/Preferences if the window is not already displayed on the 
  3841. screen). 
  3842.  
  3843. In this window a notebook control is displayed providing some (editable) 
  3844. information about the currently selected object. Every time you select another 
  3845. object (or user interface control in a window) the display area of the notebook 
  3846. pages is updated to reflect the correct set of properties and the actual 
  3847. settings for the currently selected object. The first preferences page for a 
  3848. main window is shown in Figure~fig:main-window-prefs. 
  3849.  
  3850.  
  3851.  
  3852. First Page of the Window Preferences of a MainWindow. This page contains controls for the most commonly usedproperties of such an object.
  3853.  
  3854. The following description assumes that the main window created previously is 
  3855. selected. On the first page of the notebook in the preferences window you can 
  3856. find some generic information about the selected object, such as the text 
  3857. displayed in the title bar of the window (Title), its PM id (ID) and some 
  3858. flags. 
  3859.  
  3860. For the moment, just change the title text (in the Preferences window, not in 
  3861. the main window of the interface file) to Textview and its PM id to 1000. After 
  3862. doing so, check the boxes placed on the left sides of Menu and Visible on 
  3863. startup. Then click on Change in the lower right corner of the notebook page to 
  3864. actually perform the changes. 
  3865.  
  3866. These actions (the dialog as shown after doing the changes is depicted in 
  3867. Figure~fig:main-window-prefs) will cause the window title to be changed to 
  3868. Textview and the PM resource identifier to 1000. In addition the flag FCF_MENU 
  3869. is set and Visible on startup causes the window to be displayed automatically 
  3870. when the interface file is loaded by the application (normally when the 
  3871. application is started). Most of the changes made in the Preferences window are 
  3872. displayed immediately. Only those settings requiring access to a resource file 
  3873. (as it is the case for menu resources at the moment) will only be activated 
  3874. when the application program has been compiled, linked and is being started. 
  3875.  
  3876. Next click on the small arrow at the bottom of the page pointing to the right 
  3877. side to switch to the next notebook page, the secondary window preferences. On 
  3878. this page, as you can see in Figure~fig:main-window-secprefs, you can change 
  3879. most of the other flags normally specified when calling -initWithId:andFlags: 
  3880. "manually" by simply checking or unchecking some of the boxes left to the 
  3881. description 
  3882.  
  3883.  
  3884.  
  3885.  
  3886. Secondary Window Preferences Page of a Main Window
  3887.  
  3888. In addition to the flags set automatically you should check the boxes next to 
  3889. Tasklist and Automatic Positioning. It should be clear what effect setting 
  3890. these flags will have on the window object. 
  3891.  
  3892. For a more detailed description of the available flags see the Application 
  3893. Programming Tools Manual and the Reference Manual. 
  3894.  
  3895. After correctly setting up the properties of the main window, the multi-line 
  3896. entry field must be dealt with. To display the preferences of this object, 
  3897. click once with button one on the object as displayed inside the main window. 
  3898. You will notice that a thin border is drawn around the object indicating that 
  3899. now this object is the selected user interface control in its parent window. 
  3900.  
  3901. Immediately, a new Window Properties page is displayed in the Preferences 
  3902. window already reflecting the current settings of the multi-line entry field. 
  3903.  
  3904.  
  3905.  
  3906.  
  3907. Window Properties Page of a Multi-Line Entry Field.
  3908.  
  3909. You should make sure that the check box read-only is in checked state, so that 
  3910. the settings of the multi-line entry field are the same as shown in the window 
  3911. in Figure~fig:mle-prefs. Additionally, you should add horizontal and vertical 
  3912. scrollbars by activation the appropriate check boxes. 
  3913.  
  3914. You will have noticed that you can find a button labelled Change at the bottom 
  3915. of some of the preferences pages. You are only required to depress this button 
  3916. when editing the data in one of the entry fields on a preferences page. Changes 
  3917. to a check box or a radio button are immediately performed on the window object 
  3918. without requiring any further user action. 
  3919.  
  3920. Now the user whole user interface is created, as you would expect when using a 
  3921. normal dialog editor. In the next section you will learn about making 
  3922. connections between the objects in an interface file and how this can simplify 
  3923. your life when creating OS/2 programs. 
  3924.  
  3925.  
  3926. ΓòÉΓòÉΓòÉ 10.3.6. Objective C---Connecting Objects ΓòÉΓòÉΓòÉ
  3927.  
  3928. In previous chapters the design philosophy of the library package was 
  3929. introduced. Every user action is normally processed by one of the standard PM 
  3930. classes in the beginning. If an object thinks it is not able to react to a user 
  3931. action as expected then it will try to send a message to some other object (in 
  3932. plain C you would call this method a call-back function) letting it perform the 
  3933. additional work. Which method is called is either specified by the user or by 
  3934. the sending object itself. 
  3935.  
  3936. The first case is used for user actions issuing some kind of command. The 
  3937. application programmer has to decide which method of some object should be 
  3938. called in case the user issues a special command. You can set up such 
  3939. command-action bindings by using the methods -bindCommand:withObject:selector: 
  3940. (for standard, main and dialog windows) or -bindWith:selector: in case the 
  3941. command is to be issued from a button control. Previously, command-action 
  3942. bindings were used to connect menu items or button controls with special 
  3943. methods to be executed when the item was chosen or the button was pressed. It 
  3944. must be noted that the term command-action binding will only indicate a binding 
  3945. directly originating from the sending object itself, i.e., bindings set up via 
  3946. -bindCommand:withObject:selector: will not fall into this category. 
  3947.  
  3948. The other way of sending a message to some object is called delegating. An 
  3949. object will delegate some of its functions to another object. Only the object 
  3950. must be specified by the application programmer. The sending object knows, what 
  3951. special methods to call if it is required to do so. Delegate objects are 
  3952. normally set using the -setDelegate: method. Connections of this kind are 
  3953. called outlet connections. You will remember that in the original "Textview" 
  3954. application the delegate object of the main window provided a method 
  3955. -windowDidResize: which was called whenever the main window was resized. As a 
  3956. consequence, this method adapted the size of the multi-line entry field to fill 
  3957. the complete client area of the main window. 
  3958.  
  3959. It has already been mentioned before that the Interface Editor program provides 
  3960. more functionality than a normal dialog editor. In addition to creating 
  3961. instances of user-defined classes, the Interface Editor can also be used to set 
  3962. up command-action bindings and outlet bindings. A command-action binding is 
  3963. defined to be a connection between two objects uniquely connecting a command 
  3964. (represented by an instance variable of type (Command) in the sending object) 
  3965. with a specific action method of the target object. Outlet bindings are simply 
  3966. connections of an outlet instance variable (an instance variable of type (id) 
  3967. in the source object) with a target object. Every time a command is issued, the 
  3968. associated action method of the target object is automatically called, and in 
  3969. case of an outlet binding, the outlet instance variable is initialized to be a 
  3970. pointer to the target object. 
  3971.  
  3972. To set up a command-action binding or an outlet binding you can simply use the 
  3973. OS/2 direct manipulation facilities. Move the mouse pointer to the source 
  3974. object (either a window object or an object in the main window of the interface 
  3975. file) and make an object reference to the target object as you would do to make 
  3976. an object reference on the workplace shell. You can either press the Ctrl and 
  3977. the Shift key and "drag" the object with the mouse, or simply press mouse 
  3978. button 3 (normally the middle mouse button) while moving the pointer from the 
  3979. source object to the target object. 
  3980.  
  3981.  
  3982.  
  3983.  
  3984. Connecting the Main Window with the Multi-line Entry Field
  3985.  
  3986. In addition to implementing a delegate method called -windowDidResize: you can 
  3987. use the special outlet instance variable clientWindow to cause a window object 
  3988. to always fill the complete client area of a main window. To automatically 
  3989. adapt the size of the multi-line entry field to the size of its parent window 
  3990. (the "Textview" main window in our case), make an outlet connection from the 
  3991. main window to the multi-line entry field using the outlet clientWindow. 
  3992. Figure~fig:main-window-connect shows how connecting objects will look. 
  3993.  
  3994. After "dropping" the main window on the multi-line entry field, a dialog 
  3995. letting you choose the command-action or outlet to connect will appear on the 
  3996. screen. This dialog is depicted in Figure~fig:main-window-connect-dialog. 
  3997.  
  3998.  
  3999.  
  4000.  
  4001. Dialog for connecting the Main Window of the "Textview"application with the Multi-line Entry Field
  4002.  
  4003. In the list box control on the left a list of outlets (and even of available 
  4004. commands) is presented. The right list box will be used to choose the 
  4005. appropriate action if a command-action binding is to be established. In our 
  4006. case, an outlet connection from the main window to the multi-line entry field 
  4007. via the outlet clientWindow shall be established. Select the appropriate item 
  4008. (clientWindow) in the Outlets list box and press Ok. This will cause the outlet 
  4009. connection to be established. 
  4010.  
  4011. The list box will display all available outlet instance variables and commands. 
  4012. You can differ these two kinds of items by the way they are presented. Any 
  4013. variable representing a command is put between brackets, so if delegate was a 
  4014. command instead of an outlet, it would be displayed as (delegate). Variables 
  4015. already connected are preceded by a *. The next time you will try to make a 
  4016. connection originating from the main window, the first item in the list box to 
  4017. the left will be displayed as *clientWindow. To change a connection, you can 
  4018. simply overwrite it by choosing it when making another connection. Deleting a 
  4019. connection is achieved by using the Preferences window. All connections are 
  4020. displayed on a page labelled with Object Connections (the tab text of this page 
  4021. is Conn.). Using the Disconnect button displayed there you can delete a 
  4022. connection. This is described in more detail in the Application Programming 
  4023. Tools Manual. 
  4024.  
  4025.  
  4026.  
  4027.  
  4028. Object Connections page of the Preferenceswindow for the Controller object after making the firstoutlet connection
  4029.  
  4030. In addition to the single outlet connection already established, the two outlet 
  4031. instance variables of the Controller object must be initialized. Make an outlet 
  4032. connection for the variable mainWindow from Controller to the main window and 
  4033. another connection for mleWindow from the same source object to the multi-line 
  4034. entry field. 
  4035.  
  4036. To get the right sense for these object connections, have a look at 
  4037. Figure~fig:controller-connections. There the Object Connections page for the 
  4038. Controller object after making the first outlet connection is shown. You can 
  4039. see that the name of one outlet instance variable is preceded by a *, marking 
  4040. it as being the source of a connection. 
  4041.  
  4042. After connecting the object, the work with the Interface Editor program is 
  4043. finished, you can now save the data by using File/Save and exit the program. 
  4044. Now we will return to the Project Editor and start editing the source code. 
  4045.  
  4046.  
  4047. ΓòÉΓòÉΓòÉ 10.4. Editing the Source Code of the Program ΓòÉΓòÉΓòÉ
  4048.  
  4049. You should have saved your interface file by now and eventually closed the 
  4050. Interface Editor program---you will not need it any more when finishing this 
  4051. rewrite of "Textview". 
  4052.  
  4053. After bringing the Project Editor to the front again, you can see two file 
  4054. icons on the Classes page of the project notebook. These icons---as depicted in 
  4055. Figure~fig:textview-project-classes---represent the two implementation files 
  4056. part of the project, the file containing the main() function, "Main.m", and the 
  4057. implementation of our Controller class, "Controller.m". 
  4058.  
  4059.  
  4060.  
  4061.  
  4062. Icons in the Classes Page of the Project Editor ProjectNotebook
  4063.  
  4064. As the main project source code file is suitable for most applications you will 
  4065. develop, including "Textview", we will only have to edit the class 
  4066. implementation file of Controller. To start editing, simply double-click on the 
  4067. icon of "Controller.m" in the Project Editor application. The designated text 
  4068. editor for class implementation files---see the Application Programming Tools 
  4069. Manual for information on customizing the external programs---will be started 
  4070. and the implementation file "Controller.m" is loaded automatically. 
  4071.  
  4072. The only method currently defined for Controller is the action method 
  4073. -loadFile:. The method template created by the Project Editor automatically 
  4074. must be filled in to provide the expected functionality. As the implementation 
  4075. of this method was already discussed when describing the "Textview" application 
  4076. for the first time, no further explanation is required. The source code should 
  4077. be modified to look like this: 
  4078.  
  4079. -loadFile: sender
  4080. {
  4081. FileDlg *fileDlg = [[FileDlg alloc] initForOpen: Open File...
  4082. withFilter: *.* ];
  4083. if (([fileDlg runModalFor: mainWindow]) && ([fileDlg result] == DID_OK))
  4084. [self readAndDisplayFile: [fileDlg fileName]];
  4085.  
  4086. [fileDlg free];
  4087.  
  4088. return self;
  4089. }
  4090.  
  4091. The -readAndDisplayFile: method was already discussed previously. Because it is 
  4092. only used internally by objects of the class Controller, the source code should 
  4093. be placed in the section reserved for private methods. 
  4094.  
  4095. -readAndDisplayFile: (char *) fileName
  4096. {
  4097. char *title;
  4098. FILE *inputFile;
  4099. struct stat statbuffer;
  4100. char *contents;
  4101.  
  4102. if (stat (fileName,&statbuffer) < 0) { /* check file */
  4103.  
  4104. [mleWindow setText: ];
  4105. [mainWindow setTitle: Textview: no file loaded ];
  4106.  
  4107. return nil;
  4108. }
  4109.  
  4110. /*
  4111. * open file and read contents to buffer
  4112. */
  4113. inputFile = fopen (fileName, r ); /* open text file read-only */
  4114.  
  4115. contents = (char *) malloc (statbuffer.st_size + 1); /* allocate buffer */
  4116. fread (contents,statbuffer.st_size,1,inputFile); /* read contents of file */
  4117.  
  4118. /*
  4119. * calculate title of window and set it
  4120. */
  4121. title = (char *) malloc (11 + /* this is the length of Textview: +
  4122. NULL */
  4123. strlen (fileName)); /* allocate buffer
  4124. for title */
  4125. sprintf (title, Textview:  ,fileName); /* fill title buffer */
  4126. [mleWindow setText: contents]; /* display contents of file */
  4127. [mainWindow setTitle: title];
  4128.  
  4129. free (title);
  4130. free (contents);
  4131.  
  4132. fclose (inputFile); /* close file */
  4133.  
  4134. return self;
  4135. }
  4136.  
  4137. Defining a new method requires---just for the sake of a good programming 
  4138. practice---to insert a method declaration in the appropriate header file. To do 
  4139. this, double-click on the icon representing "Controller.h" in the Headers 
  4140. section of the project notebook and add 
  4141.  
  4142. -readAndDisplayFile: (char *) fileName;
  4143.  
  4144. to the interface declaration of the class Controller (also put the declaration 
  4145. in the private methods section). 
  4146.  
  4147. What is still to do now is to bind the menu items to the appropriate objects 
  4148. and methods. At the moment there is no way to bind menu items graphically to 
  4149. action methods, therefore your application must issue some 
  4150. -bindCommand:withObject:selector: statements in an appropriate place in the 
  4151. source code itself. Remember, that this limitation does only apply to binding 
  4152. commands originating from menu items. Commands initiated by e.g. button 
  4153. controls can be dealt with by just setting up a command-action binding using 
  4154. the Interface Editor application. 
  4155.  
  4156. Normally, you would place setting up these bindings into the initialization 
  4157. method of the Controller class. The problem is, that at the time the -init 
  4158. method is processed, the outlet bindings originating from the object itself are 
  4159. not initialized. Therefore there must be a method called after completely 
  4160. initializing an object and all in- and outgoing command-action and outlet 
  4161. bindings. This method is called -awakeFromInterfaceFile. For every object 
  4162. created from a user-defined class using the Interface Editor, this method is 
  4163. called---if implemented---after loading and creating it from an InterfaceFile. 
  4164. The only thing you must do to perform a custom initialization for an object 
  4165. after all bindings have been set up correctly, is to write an 
  4166. -awakeFromInterfaceFile method for your classes. 
  4167.  
  4168. In the case of Controller, the method will simply bind the menu items Open... 
  4169. and Exit with the appropriate methods of the target objects. An 
  4170. implementation---this time to be put into the section reserved for the 
  4171. Archiving methods at the end of the class implementation file---will just 
  4172. consist of two calls to -bindCommand:withObject:selector:: 
  4173.  
  4174. -awakeFromInterfaceFile
  4175. {
  4176. [mainWindow bindCommand: 2001 withObject: self
  4177. selector: @selector (loadFile:)];
  4178. [mainWindow bindCommand: 2002 withObject: mainWindow
  4179. selector: @selector (performClose:)];
  4180.  
  4181. return self;
  4182. }
  4183.  
  4184. Just as before, put the prototype declaration of the method, 
  4185.  
  4186. -awakeFromInterfaceFile;
  4187.  
  4188. into the header file "Controller.h". 
  4189.  
  4190. After these changes to the interface and the implementation of the class have 
  4191. been made, everything is done and we can use Project Editor to compile and 
  4192. start the program. 
  4193.  
  4194.  
  4195. ΓòÉΓòÉΓòÉ 10.5. Compiling and Linking the Application ΓòÉΓòÉΓòÉ
  4196.  
  4197. To compile and link the application first have a look at 
  4198. Figure~fig:project-compile-menu. There you can see the Project Editor 
  4199. application and one of its menus. In the compile menu you can find some menu 
  4200. items all concerned with compiling and starting the application you are 
  4201. developing. 
  4202.  
  4203.  
  4204.  
  4205.  
  4206. Project Editor Application with the Compile Menu beingopen. To compile and link the project, just choose Build,to start the program, click on Run
  4207.  
  4208. Using 
  4209.  
  4210.      Build you simply compile and link the application. This is equivalent to 
  4211.       typing "make" on the command line. The object files are created with full 
  4212.       optimization on (compiler flag -O2). 
  4213.      Debug is used to build an executable file including debug code. You can 
  4214.       use gdb to debug your application. Always keep in mind, that debugging of 
  4215.       PM applications requires gdb to be started in a full-screen session. 
  4216.  
  4217.       I experienced some troubles when using normal GNU make for compiling 
  4218.       using debug. My version complains about too many open files. I will try 
  4219.       to find out, if my version is just a bit out of date, or there is a bug I 
  4220.       can fix. 
  4221.      Clean will remove all object files, compiled resource files and the 
  4222.       executable file. Additionally backup files as created by GNU Emacs 
  4223.       (ending with a ~) will be deleted. This is recommended after compilation 
  4224.       was stopped using 
  4225.      Abort. This menu item will stop compilation, or, in case your program is 
  4226.       already started, will terminate the program. 
  4227.      Run is used to start the application. If necessary, Build is executed 
  4228.       automatically before starting the executable file. 
  4229.  
  4230.  When compiling, the Log Page of the project data is automatically shown. Here 
  4231.  error and status messages are displayed while compiling and linking. 
  4232.  Figure~fig:project-compilation-done shows this Log Page after compiling the 
  4233.  current "Textview" project. 
  4234.  
  4235.  
  4236.  
  4237.  
  4238.   Log Page of the Project Editor Program after successfulcompilation of the Project. You can now simply chooseCompile/Run to start the program you created.
  4239.  
  4240.  
  4241. ΓòÉΓòÉΓòÉ 10.6. Creating an Application---Step by Step ΓòÉΓòÉΓòÉ
  4242.  
  4243. In theory, you should now be able to start a project on your own and utilize 
  4244. the visual development tools shipped with the library package. To provide some 
  4245. help, here the necessary steps for creating and editing a project are repeated 
  4246. once more. You can see this as some kind of recipe for creating your own 
  4247. applications. 
  4248.  
  4249.    1. Create a new project by simply dragging a project template to your work 
  4250.       directory. 
  4251.    2. Open the newly created project folder and start the Project Editor by 
  4252.       double-clicking on the project icon ("PB.prj"). 
  4253.    3. Now edit the settings on the Linker Page of the project notebook. You 
  4254.       must at least set the name of the executable file and check whether all 
  4255.       library files required by your application are specified. 
  4256.    4. Open the Class Editor and create the custom classes you intend to use. 
  4257.       Unparse a source code template for every of the new classes and then save 
  4258.       the project. 
  4259.    5. Now create a new "Makefile" and start editing the main interface file 
  4260.       "main.oib" by simply double-clicking on its icon in the project folder. 
  4261.    6. Create the user interface elements you want to use and the instances of 
  4262.       the user-defined classes your application will require. 
  4263.    7. Then set up all command-action and outlet bindings and save the interface 
  4264.       file to disk. 
  4265.    8. In the end, edit the source code and fill in the method templates. Add 
  4266.       new methods, save the files to disk and choose Compile/Build from the 
  4267.       menu bar of the Project Editor. 
  4268.    9. After compilation succeeded, you can start and test your application by 
  4269.       choosing Compile/Run from the menu bar. 
  4270.  
  4271.  More information about the tools can be found in the Application Programming 
  4272.  Tools Manual. The libraries are fully documented in the Reference Manual. Also 
  4273.  have a look at the sample programs in \usr\samples. There you can find 
  4274.  additional information about programming and design. To find out about using 
  4275.  Project Editor and Interface Editor together with the database library, check 
  4276.  \usr\samples\DBView.prj and carefully examine the source code. It's really 
  4277.  simple, you will see. 
  4278.  
  4279.  
  4280. ΓòÉΓòÉΓòÉ 11. Debugging Objective C Programs ΓòÉΓòÉΓòÉ
  4281.  
  4282. No matter how good the development system is, you are using---that's not to 
  4283. say, this one is perfect---or how good and experienced you are in developing 
  4284. applications, you will of course produce erroneous source code in the 
  4285. beginning. This chapter will try to mention some tricks supporting you in 
  4286. finding these errors. 
  4287.  
  4288. First, let me say, that in case of an application not performing as expected by 
  4289. you, the most useful tool to localize the errors is a debugger. The emx 
  4290. development tools contain a very good---even if it is not that simple and 
  4291. intuitive to use as you would expect for an OS/2 program---debugger called gdb, 
  4292. the GNU Debugger. But for most simple cases, using gdb is just a bit too much. 
  4293.  
  4294.  
  4295. ΓòÉΓòÉΓòÉ 11.1. Using "Console" ΓòÉΓòÉΓòÉ
  4296.  
  4297. When your program just silently (silently means "without OS/2 putting some 
  4298. message box onto the desktop informing you of some SYS**** error") crashes, in 
  4299. most cases you just made some simple error the Objective C runtime library 
  4300. complains of. 
  4301.  
  4302. In the current version of the Objective C runtime library, the library 
  4303. functions will print a short description of the error which occured to stderr 
  4304. and stop the program. 
  4305.  
  4306. The only problem is that normally stderr is connected with the output area of a 
  4307. shell window, and therefore such error messages can't be seen when writing PM 
  4308. applications. The only possibility to view such messages is to redirect stderr 
  4309. to some file or device other than the standard output device. 
  4310.  
  4311. If you started your application from the Project Editor, stdout as well as 
  4312. stderr will be connected to the Log Page of Project Editor. There you can see 
  4313. the messages printed by the runtime library. 
  4314.  
  4315. So, if for example you will see something like 
  4316.  
  4317. error: InterfaceFile (instance)
  4318. InterfaceFile does not recognize unknownMethod
  4319. Abnormal program termination
  4320. core dumped
  4321.  
  4322. this means that your program tried to call a method not recognized by the 
  4323. receiving object. In this special case, the error occured with an instance of 
  4324. InterfaceFile. The program tried to call the method -unknownMethod, which 
  4325. InterfaceFile cannot respond to. Therefore the runtime library printed the 
  4326. error message to stderr and exited the program. 
  4327.  
  4328. Although this does not show where in your program the error was made, you will 
  4329. surely be able to localize the error by just thinking on where and how you used 
  4330. instances of the class issuing the error. 
  4331.  
  4332. For applications not started from within the Project Editor, a small program is 
  4333. provided, which just displays everything sent to a special file called 
  4334. "\pipe\console". After starting the program Console, this (virtual) file is 
  4335. created and you can write text data to it. To display the messages printed to 
  4336. stderr by your program in the Console window, you will have to redirect stderr 
  4337. to "\pipe\console". Starting an application called "test.exe" and redirecting 
  4338. stderr can be accomplished by typing 
  4339.  
  4340. test 2> \pipe\console 
  4341.  
  4342. in the directory "test.exe" resides. 
  4343.  
  4344. Redirecting stderr can also provide useful when your application program wants 
  4345. to print some debugging information. To write strings or other data to the 
  4346. Console window after stderr has been redirected to \pipe\console, use 
  4347.  
  4348. fprintf (stderr,...);
  4349.  
  4350. in your programs. 
  4351.  
  4352. For more complex programs or errors, you are not able to localize within some 
  4353. seconds, you should use the debugger gdb. 
  4354.  
  4355.  
  4356. ΓòÉΓòÉΓòÉ 11.2. Using "gdb" ΓòÉΓòÉΓòÉ
  4357.  
  4358. To be able to use gdb to debug your applications, you must create debugging 
  4359. code while compiling and linking your program. This is done automatically when 
  4360. you specify the option -g on the gcc command line used for compiling and 
  4361. linking. 
  4362.  
  4363. To preserve debugging information, you may not strip the symbol table from the 
  4364. executable file (emxbind -s *) after linking. Choosing Compile/Debug from the 
  4365. menu bar of the Project Editor will compile all modified source code files and 
  4366. create an executable file suited for debugging called "debug.exe". If you 
  4367. experience problems, i.e., the files you want to debug are not automatically 
  4368. recompiled, choose Compile/Clean before using Compile/Debug. 
  4369.  
  4370. After the executable file including debug information has been created, start 
  4371. gdb in an OS/2 full-screen session (You must start gdb in an OS/2 full-screen 
  4372. session to debug PM applications, otherwise your system will hang while 
  4373. debugging and you will have to reboot) by typing 
  4374.  
  4375. gdb debug.exe
  4376.  
  4377. gdb does not speak Objective C at the moment, that means, the debugger does not 
  4378. know the syntax of the Objective C language. Therefore, you have to debug your 
  4379. applications just as if they were written in plain C. 
  4380.  
  4381. The methods of your classes are represented as normal C functions. The names of 
  4382. the functions are constructed from 
  4383.  
  4384.      the type of the method, which is either an instance or a factory (class) 
  4385.       method, 
  4386.      the name of the class the method belongs to, and 
  4387.      the name of the method itself. 
  4388.  
  4389.  Every name is of the form 
  4390.  
  4391.   _<type>_<name of class>__<name of method>
  4392.  
  4393.  where <type> is either i for an instance method or c for a factory method. In 
  4394.  the method name, every colon (:) is replaced by an underscore (_). To clarify 
  4395.  this form of notation, a simple example will be presented: 
  4396.  
  4397.  To set a breakpoint in the method -loadFile: of the Controller class you would 
  4398.  use the b (break) command of gdb. So, simply type 
  4399.  
  4400.   b _i_Controller__loadFile_
  4401.  
  4402.  at the gdb command prompt. For a class method called +create:andInsertInto:, 
  4403.  you would have to type 
  4404.  
  4405.   b _c_Controller__create_andInsertInto_
  4406.  
  4407.  Instance variables are stored in some kind of struct. self is a pointer to 
  4408.  this structure, so you could print the instance variable text of the current 
  4409.  object by typing 
  4410.  
  4411.   p self->text
  4412.  
  4413.  Which object is the current object just depends on the function you are 
  4414.  debugging. 
  4415.  
  4416.  More information on debugging Objective C programs can be found in the 
  4417.  Objective C FAQ. 
  4418.  
  4419.  
  4420. ΓòÉΓòÉΓòÉ 12. Literature ΓòÉΓòÉΓòÉ
  4421.  
  4422. If you are searching for good books about the programming language Objective C 
  4423. itself, and you have access to any machine running NEXTSTEP, try reading the 
  4424. according sections of the NEXTDEVELOPER manual pages. An easy to understand 
  4425. document about Objective C and its rootclass can be found there. 
  4426.  
  4427. Another good introduction into Objective C is a book by Brad J. Cox, who 
  4428. specified the language itself, Object-Oriented Programming, An Evolutionary 
  4429. Approach, second edition. Addison Wesley, 1991. 
  4430.  
  4431. German users should look for a copy of the December 1994 issue of the magazine 
  4432. iX. You can find a (mostly) good overview of Objective C and the implementation 
  4433. found with GCC. 
  4434.  
  4435. Some information concerning Objective C or special questions towards using this 
  4436. language can be found in comp.lang.objective-c. An FAQ on Objective C in 
  4437. general and on the GNU implementation of this language is posted regularly. 
  4438. General information in the world wide web can be found at 
  4439.  
  4440.      http://www.marble.com/~dekorte/dekorte/Objective-C (Objective C "home 
  4441.       page"), 
  4442.      http://www.geom.umn.edu/software/w3kit/overview/objective-c.html (a short 
  4443.       introduction into Objective C) and many other places in the web. 
  4444.  
  4445.  Work on an Objective C class library providing some Smalltalk-like classes is 
  4446.  being done now. Check for an OS/2 port of libobjects on Hobbes (anonymous ftp 
  4447.  server hobbes.nmsu.edu). This library provides many classes simplifying 
  4448.  handling of object storage (e.g. List classes, HashTable,...). 
  4449.  
  4450.  At the moment, it is recommended to read some documentation about PM 
  4451.  programming. The documentation for this toolbox and the classes themselves are 
  4452.  not as complete, as they will be in the near future. Nevertheless, they are 
  4453.  quite usable to create some simple---and by capturing some OS/2 PM 
  4454.  messages---also more complex Presentation Manager applications. To find out 
  4455.  more about "pure PM programming" get the issues of the Electronic Developer's 
  4456.  Magazine, which also can be found on Hobbes. Most information ever needed for 
  4457.  PM programming can be in the documentation accompanying the IBM OS/2 
  4458.  developer's toolkit. An introduction into PM programming, its concepts and the 
  4459.  API functions provided by OS/2 can also be found in one of the Redbooks, OS/2 
  4460.  Version 2.0; Volume 4: Application Development. This book can be found in 
  4461.  .INF-format on Hobbes. 
  4462.  
  4463.  Before sending any questions to me, be sure to read all of this manual and the 
  4464.  reference manual. Also have a look at the sample programs, which can be found 
  4465.  in \usr\samples. Some of the samples stored there are not described in this 
  4466.  manual, but can contain some information, you might need. 
  4467.  
  4468.